Add PayPal webhook handler and marketing styles

- 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.
This commit is contained in:
“VeLiTi”
2026-01-09 15:19:28 +01:00
parent 08263c7933
commit 2520fb2b75
38 changed files with 4166 additions and 1107 deletions

BIN
api/.DS_Store vendored

Binary file not shown.

BIN
api/v1/.DS_Store vendored

Binary file not shown.

BIN
api/v2/.DS_Store vendored

Binary file not shown.

View File

@@ -0,0 +1,116 @@
<?php
defined($security_key) or exit;
// Database connection
$pdo = dbConnect($dbname);
// ============================================
// Input Validation & Sanitization
// ============================================
$filters = [
'serialnumber' => isset($_GET['serialnumber']) ? trim($_GET['serialnumber']) : null,
'type' => isset($_GET['type']) ? trim($_GET['type']) : null,
'start' => isset($_GET['start']) ? trim($_GET['start']) : date("Y-m-d", strtotime("-270 days")),
'end' => isset($_GET['end']) ? trim($_GET['end']) : date("Y-m-d", strtotime("+1 days"))
];
// ============================================
// Build Query with Prepared Statements
// ============================================
$whereClauses = [];
$params = [];
// Serial Number Filter
if ($filters['serialnumber']) {
$whereClauses[] = 'h.description LIKE :serialnumber';
$params[':serialnumber'] = "%historycreated%SN%:" . $filters['serialnumber'] . "%";
$whereClauses[] = 'h.type != :excluded_type';
$params[':excluded_type'] = 'SRIncluded';
}
// Type Filter
if ($filters['type']) {
if ($filters['type'] === 'latest') {
// Get only the latest record per equipment
if ($filters['serialnumber']) {
$whereClauses[] = 'h.rowID IN (
SELECT MAX(h2.rowID)
FROM equipment_history h2
GROUP BY h2.equipmentid
)';
} else {
$whereClauses[] = "h.description LIKE '%historycreated%'";
$whereClauses[] = 'h.rowID IN (
SELECT MAX(h2.rowID)
FROM equipment_history h2
WHERE h2.description LIKE :history_created
GROUP BY h2.equipmentid
)';
$params[':history_created'] = '%historycreated%';
}
} else {
// Specific type filter
$whereClauses[] = 'h.type = :type';
$params[':type'] = $filters['type'];
}
}
// Default filter if no other filters applied
if (empty($whereClauses)) {
$whereClauses[] = "h.description LIKE '%historycreated%'";
}
// Date Range Filter
$whereClauses[] = 'h.created BETWEEN :start_date AND :end_date';
$params[':start_date'] = $filters['start'];
$params[':end_date'] = $filters['end'];
// ============================================
// Execute Query
// ============================================
$whereClause = 'WHERE ' . implode(' AND ', $whereClauses);
$sql = "SELECT h.rowID, h.description
FROM equipment_history h
$whereClause
ORDER BY h.created DESC";
try {
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
// ============================================
// Format Response
// ============================================
$results = [];
foreach ($messages as $message) {
$record = json_decode($message['description'], true);
// Handle JSON decode errors
if (json_last_error() !== JSON_ERROR_NONE) {
continue; // Skip invalid JSON
}
$record['historyID'] = (int)$message['rowID'];
$results[] = $record;
}
// Set proper headers
header('Content-Type: application/json; charset=utf-8');
echo json_encode($results, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
} catch (PDOException $e) {
// Log error (don't expose to client in production)
error_log("Database error: " . $e->getMessage());
//header('Content-Type: application/json; charset=utf-8', true, 500);
echo json_encode([
'error' => 'An error occurred while processing your request'
]);
}
?>

View File

@@ -275,7 +275,7 @@ else {
}
//SQL for Paging
$sql = 'SELECT e.rowID as equipmentID, e.*, p.productcode, p.productname, p.product_media from equipment e LEFT JOIN products p ON e.productrowid = p.rowID '.$whereclause.' ORDER BY '.$sort.' LIMIT :page,:num_products';
$sql = 'SELECT e.rowID as equipmentID, e.*, p.productcode, p.productname, p.product_media, psl.starts_at,psl.expires_at,psl.status as license_status from equipment e LEFT JOIN products p ON e.productrowid = p.rowID LEFT JOIN products_software_licenses psl ON e.sw_version_license = psl.license_key '.$whereclause.' ORDER BY '.$sort.' LIMIT :page,:num_products';
}
$stmt = $pdo->prepare($sql);

View File

@@ -0,0 +1,155 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// Marketing Files
//------------------------------------------
//Connect to DB
$pdo = dbConnect($dbname);
//SoldTo is empty
if (empty($partner->soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';}
//default whereclause
$whereclause = '';
// For testing, disable account hierarchy filtering
// list($whereclause,$condition) = getWhereclauselvl2("",$permission,$partner,'get');
//NEW ARRAY
$criterias = [];
$clause = '';
//Check for $_GET variables and build up clause
if(isset($get_content) && $get_content!=''){
//GET VARIABLES FROM URL
$requests = explode("&", $get_content);
//Check for keys and values
foreach ($requests as $y){
$v = explode("=", $y);
//INCLUDE VARIABLES IN ARRAY
$criterias[$v[0]] = $v[1];
if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] == 'action' || $v[0] =='success_msg' || $v[0] == '_t'){
//do nothing
}
elseif ($v[0] == 'folder_id') {
if ($v[1] === 'null' || $v[1] === '') {
$clause .= ' AND folder_id IS NULL';
} else {
$clause .= ' AND folder_id = :folder_id';
}
}
elseif ($v[0] == 'search') {
$clause .= ' AND (title LIKE :search OR original_filename LIKE :search)';
}
elseif ($v[0] == 'tag') {
$clause .= ' AND EXISTS (SELECT 1 FROM marketing_file_tags ft JOIN marketing_tags t ON ft.tag_id = t.id WHERE ft.file_id = mf.id AND t.tag_name = :tag)';
}
elseif ($v[0] == 'file_type') {
$clause .= ' AND file_type = :file_type';
}
else {
// Ignore unknown parameters
}
}
if ($whereclause == '' && $clause !=''){
$whereclause = 'WHERE '.substr($clause, 4);
} else {
$whereclause .= $clause;
}
}
//Set page
$pagina = 1;
if(isset($criterias['p']) && $criterias['p'] !='') {
$pagina = $criterias['p'];
}
//Set limit
$limit = 50;
if(isset($criterias['limit']) && $criterias['limit'] !='') {
$limit = intval($criterias['limit']);
}
$offset = ($pagina - 1) * $limit;
//check for totals call
if(isset($criterias['totals'])){
$sql = 'SELECT COUNT(*) as found FROM marketing_files mf '.$whereclause.' ';
$stmt = $pdo->prepare($sql);
// Bind parameters
if (!empty($criterias)) {
foreach ($criterias as $key => $value) {
if ($key !== 'totals' && $key !== 'page' && $key !== 'p' && $key !== 'limit' && $key !== 'action') {
if ($key == 'search') {
$stmt->bindValue(':'.$key, '%'.$value.'%');
} elseif ($key == 'folder_id' && ($value === 'null' || $value === '')) {
continue;
} else {
$stmt->bindValue(':'.$key, $value);
}
}
}
}
$stmt->execute();
$found = $stmt->fetchColumn();
echo $found;
exit;
}
// Main query
$sql = "SELECT
mf.*,
GROUP_CONCAT(mt.tag_name) as tags
FROM marketing_files mf
LEFT JOIN marketing_file_tags mft ON mf.id = mft.file_id
LEFT JOIN marketing_tags mt ON mft.tag_id = mt.id
" . $whereclause . "
GROUP BY mf.id
ORDER BY mf.created DESC
LIMIT " . $limit . " OFFSET " . $offset;
$stmt = $pdo->prepare($sql);
// Bind parameters
if (!empty($criterias)) {
foreach ($criterias as $key => $value) {
if ($key !== 'totals' && $key !== 'page' && $key !== 'p' && $key !== 'limit') {
if ($key == 'search') {
$stmt->bindValue(':'.$key, '%'.$value.'%');
} elseif ($key == 'folder_id' && ($value === 'null' || $value === '')) {
continue;
} else {
$stmt->bindValue(':'.$key, $value);
}
}
}
}
$stmt->execute();
$marketing_files = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Process each file
foreach ($marketing_files as &$file) {
// Process tags
$file['tags'] = $file['tags'] ? explode(',', $file['tags']) : [];
// Format file size
$bytes = $file['file_size'];
if ($bytes >= 1073741824) {
$file['file_size_formatted'] = number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
$file['file_size_formatted'] = number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
$file['file_size_formatted'] = number_format($bytes / 1024, 2) . ' KB';
} else {
$file['file_size_formatted'] = $bytes . ' B';
}
}
// Return result
echo json_encode($marketing_files, JSON_UNESCAPED_UNICODE);
exit;

View File

@@ -0,0 +1,172 @@
<?php
defined($security_key) or exit;
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
//------------------------------------------
// Marketing Folders
//------------------------------------------
//Connect to DB
$pdo = dbConnect($dbname);
// Function to build hierarchical tree structure
function buildFolderTree($folders, $parentId = null) {
$tree = [];
foreach ($folders as $folder) {
if ($folder['parent_id'] == $parentId) {
$children = buildFolderTree($folders, $folder['id']);
$folder['children'] = $children; // Always include children array, even if empty
$tree[] = $folder;
}
}
return $tree;
}
//SoldTo is empty
if (empty($partner->soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';}
//default whereclause
$whereclause = '';
list($whereclause,$condition) = getWhereclauselvl2('',$permission,$partner,'get');
//NEW ARRAY
$criterias = [];
$clause = '';
//Check for $_GET variables and build up clause
if(isset($get_content) && $get_content!=''){
//GET VARIABLES FROM URL
$requests = explode("&", $get_content);
//Check for keys and values
foreach ($requests as $y){
$v = explode("=", $y);
//INCLUDE VARIABLES IN ARRAY
$criterias[$v[0]] = $v[1];
if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] =='success_msg' || $v[0] == 'action' || $v[0] == 'tree'){
//do nothing - these are not SQL parameters
}
elseif ($v[0] == 'parent_id') {
if ($v[1] === 'null' || $v[1] === '') {
$clause .= ' AND parent_id IS NULL';
} else {
$clause .= ' AND parent_id = :parent_id';
}
}
elseif ($v[0] == 'search') {
$clause .= ' AND (folder_name LIKE :search OR description LIKE :search)';
}
else {//create clause
$clause .= ' AND '.$v[0].' = :'.$v[0];
}
}
if ($whereclause == '' && $clause !=''){
$whereclause = 'WHERE '.substr($clause, 4);
} else {
$whereclause .= $clause;
}
}
//Define Query
if(isset($criterias['totals']) && $criterias['totals'] ==''){
//Request for total rows
$sql = 'SELECT count(*) as count FROM marketing_folders '.$whereclause.'';
}
elseif (isset($criterias['list']) && $criterias['list'] =='') {
//SQL for list (no paging)
$sql = "SELECT
mf.*,
(SELECT COUNT(*) FROM marketing_files WHERE folder_id = mf.id) as file_count,
(SELECT COUNT(*) FROM marketing_folders WHERE parent_id = mf.id) as subfolder_count,
CASE
WHEN mf.parent_id IS NOT NULL THEN
(SELECT folder_name FROM marketing_folders WHERE id = mf.parent_id)
ELSE NULL
END as parent_folder_name
FROM marketing_folders mf
" . $whereclause . "
ORDER BY mf.folder_name ASC";
}
else {
//SQL for paging
$sql = "SELECT
mf.*,
(SELECT COUNT(*) FROM marketing_files WHERE folder_id = mf.id) as file_count,
(SELECT COUNT(*) FROM marketing_folders WHERE parent_id = mf.id) as subfolder_count,
CASE
WHEN mf.parent_id IS NOT NULL THEN
(SELECT folder_name FROM marketing_folders WHERE id = mf.parent_id)
ELSE NULL
END as parent_folder_name
FROM marketing_folders mf
" . $whereclause . "
ORDER BY mf.folder_name ASC
LIMIT :page,:num_folders";
}
$stmt = $pdo->prepare($sql);
//Bind to query
if (str_contains($whereclause, ':condition')){
$stmt->bindValue('condition', $condition, PDO::PARAM_STR);
}
if (!empty($criterias)){
foreach ($criterias as $key => $value){
$key_condition = ':'.$key;
if (str_contains($whereclause, $key_condition)){
if ($key == 'search'){
$search_value = '%'.$value.'%';
$stmt->bindValue($key, $search_value, PDO::PARAM_STR);
}
elseif ($key == 'parent_id' && ($value === 'null' || $value === '')) {
// Skip binding for NULL parent_id
continue;
}
else {
$stmt->bindValue($key, $value, PDO::PARAM_STR);
}
}
}
}
//Add paging details
if(isset($criterias['totals']) && $criterias['totals']==''){
$stmt->execute();
$messages = $stmt->fetch();
$messages = $messages[0];
}
elseif(isset($criterias['list']) && $criterias['list']==''){
//Execute Query
$stmt->execute();
//Get results
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
else {
$current_page = isset($criterias['p']) && is_numeric($criterias['p']) ? (int)$criterias['p'] : 1;
$stmt->bindValue('page', ($current_page - 1) * $page_rows_folders, PDO::PARAM_INT);
$stmt->bindValue('num_folders', $page_rows_folders, PDO::PARAM_INT);
//Execute Query
$stmt->execute();
//Get results
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// Check if tree structure is requested
if (isset($criterias['tree']) && isset($messages) && is_array($messages)) {
// Build hierarchical tree structure
$messages = buildFolderTree($messages);
}
//------------------------------------------
//JSON_ENCODE
//------------------------------------------
$messages = json_encode($messages, JSON_UNESCAPED_UNICODE);
//Send results
echo $messages;

View File

@@ -0,0 +1,115 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// Marketing Tags
//------------------------------------------
//Connect to DB
$pdo = dbConnect($dbname);
//SoldTo is empty
if (empty($partner->soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';}
//default whereclause
$whereclause = '';
// Tags are global, so no account hierarchy filtering
// list($whereclause,$condition) = getWhereclauselvl2("",$permission,$partner,'get');
//NEW ARRAY
$criterias = [];
$clause = '';
//Check for $_GET variables and build up clause
if(isset($get_content) && $get_content!=''){
//GET VARIABLES FROM URL
$requests = explode("&", $get_content);
//Check for keys and values
foreach ($requests as $y){
$v = explode("=", $y);
//INCLUDE VARIABLES IN ARRAY
$criterias[$v[0]] = $v[1];
if ($v[0] == 'page' || $v[0] =='p' || $v[0] =='totals' || $v[0] =='list' || $v[0] =='success_msg' || $v[0] == 'action'){
//do nothing
}
elseif ($v[0] == 'search') {
$clause .= ' AND tag_name LIKE :search';
}
elseif ($v[0] == 'used_only') {
if ($v[1] === 'true') {
$clause .= ' AND id IN (SELECT DISTINCT tag_id FROM marketing_file_tags)';
}
}
else {//create clause
$clause .= ' AND '.$v[0].' = :'.$v[0];
}
}
if ($whereclause == '' && $clause !=''){
$whereclause = 'WHERE '.substr($clause, 4);
} else {
$whereclause .= $clause;
}
}
//Set page
$pagina = 1;
if(isset($criterias['p']) && $criterias['p'] !='') {
$pagina = $criterias['p'];
}
//check for totals call
if(isset($criterias['totals'])){
$sql = 'SELECT COUNT(*) as found FROM marketing_tags mt '.$whereclause.' ';
$stmt = $pdo->prepare($sql);
// Bind parameters
if (!empty($criterias)) {
foreach ($criterias as $key => $value) {
if ($key !== 'totals' && $key !== 'page' && $key !== 'p' && $key !== 'used_only') {
if ($key == 'search') {
$stmt->bindValue(':'.$key, '%'.$value.'%');
} else {
$stmt->bindValue(':'.$key, $value);
}
}
}
}
$stmt->execute();
$found = $stmt->fetchColumn();
echo $found;
exit;
}
// Main query
$sql = "SELECT
mt.*,
COUNT(mft.file_id) as usage_count
FROM marketing_tags mt
LEFT JOIN marketing_file_tags mft ON mt.id = mft.tag_id
" . $whereclause . "
GROUP BY mt.id
ORDER BY mt.tag_name ASC";
$stmt = $pdo->prepare($sql);
// Bind parameters
if (!empty($criterias)) {
foreach ($criterias as $key => $value) {
if ($key !== 'totals' && $key !== 'page' && $key !== 'p' && $key !== 'used_only') {
if ($key == 'search') {
$stmt->bindValue(':'.$key, '%'.$value.'%');
} else {
$stmt->bindValue(':'.$key, $value);
}
}
}
}
$stmt->execute();
$marketing_tags = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Return result
echo json_encode($marketing_tags, JSON_UNESCAPED_UNICODE);

41
api/v2/get/service.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// Application related calls
//------------------------------------------
$request = explode('/', trim($_SERVER['PATH_INFO'],'/'));
$action = $request[2] ?? '';
if ($action == 'init'){
include './settings/systemservicetool_init.php';
echo json_encode($init);
}
elseif ($action == 'questions' && (isset($_GET['type']) && $_GET['type'] != '')){
include './settings/systemservicetool.php';
//build questions
switch ($_GET['type']) {
case 'visual':
$arrayQuestions = $arrayQuestions_visual;
break;
case 'final':
$arrayQuestions = $arrayQuestions_finalize;
break;
case 'cartest':
include './settings/systemcartest.php';
$arrayQuestions = $arrayQuestions_cartest;
break;
}
//Return JSON
echo json_encode($arrayQuestions);
}
else {
http_response_code(400);
}
?>

View File

@@ -62,6 +62,7 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
e.sw_version as current_sw_version,
e.hw_version,
e.sw_version_license,
e.sw_version_upgrade,
e.rowID as equipment_rowid
FROM equipment e
JOIN products p ON e.productrowid = p.rowID
@@ -78,6 +79,7 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
$current_sw_version = $equipment_data['current_sw_version'];
$hw_version = $equipment_data['hw_version'];
$sw_version_license = $equipment_data['sw_version_license'];
$sw_version_upgrade = $equipment_data['sw_version_upgrade'];
$equipment_rowid = $equipment_data['equipment_rowid'];
if (debug) {
@@ -85,7 +87,8 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
'product_rowid' => $product_rowid,
'productcode' => $productcode,
'current_sw_version_raw' => $current_sw_version,
'hw_version' => $hw_version
'hw_version' => $hw_version,
'sw_version_upgrade' => $sw_version_upgrade
];
}
@@ -119,6 +122,77 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
exit;
}
// Check if sw_version_upgrade is set - this overrides normal availability check
if (!empty($sw_version_upgrade)) {
if (debug) {
$debug['sw_version_upgrade_check'] = [
'sw_version_upgrade_id' => $sw_version_upgrade,
'checking_override' => true
];
}
// Check if this version exists and is active
$sql = 'SELECT
psv.rowID as version_id,
psv.version,
psv.name,
psv.description,
psv.mandatory,
psv.latest,
psv.hw_version,
psv.file_path,
psv.status
FROM products_software_versions psv
WHERE psv.rowID = ?';
$stmt = $pdo->prepare($sql);
$stmt->execute([$sw_version_upgrade]);
$upgrade_version = $stmt->fetch(PDO::FETCH_ASSOC);
if ($upgrade_version && $upgrade_version['status'] == 1) {
// Valid override found - check if different from current version
$normalized_upgrade_version = strtolower(ltrim($upgrade_version['version'], '0'));
if (debug) {
$debug['sw_version_upgrade_check']['found_version'] = [
'version' => $upgrade_version['version'],
'name' => $upgrade_version['name'],
'normalized' => $normalized_upgrade_version,
'status' => $upgrade_version['status'],
'is_different_from_current' => ($current_sw_version != $normalized_upgrade_version)
];
}
if ($current_sw_version && $normalized_upgrade_version == $current_sw_version) {
// Override version is same as current - no upgrade available
$software_available = "no";
if (debug) {
$debug['sw_version_upgrade_check']['decision'] = 'Override version is same as current version';
}
} else {
// Override version is different - upgrade is available
$software_available = "yes";
if (debug) {
$debug['sw_version_upgrade_check']['decision'] = 'Override version is available';
}
}
$messages = ["software_available" => $software_available];
if (debug) {
debuglog(json_encode($debug));
}
echo json_encode($messages, JSON_UNESCAPED_UNICODE);
exit;
} else {
// Override version not found or inactive - fall back to standard check
if (debug) {
$debug['sw_version_upgrade_check']['found_version'] = $upgrade_version ? 'found but inactive' : 'not found';
$debug['sw_version_upgrade_check']['decision'] = 'Falling back to standard check';
}
}
}
//GET ALL ACTIVE SOFTWARE ASSIGNMENTS for this product with matching HW version
$sql = 'SELECT
psv.rowID as version_id,

View File

@@ -61,6 +61,7 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
e.sw_version as current_sw_version,
e.hw_version,
e.sw_version_license,
e.sw_version_upgrade,
e.rowID as equipment_rowid
FROM equipment e
JOIN products p ON e.productrowid = p.rowID
@@ -77,6 +78,7 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
$current_sw_version = $equipment_data['current_sw_version'];
$hw_version = $equipment_data['hw_version'];
$sw_version_license = $equipment_data['sw_version_license'];
$sw_version_upgrade = $equipment_data['sw_version_upgrade'];
$equipment_rowid = $equipment_data['equipment_rowid'];
if (debug) {
@@ -85,7 +87,8 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
'productcode' => $productcode,
'current_sw_version_raw' => $current_sw_version,
'hw_version' => $hw_version,
'sw_version_license' => $sw_version_license
'sw_version_license' => $sw_version_license,
'sw_version_upgrade' => $sw_version_upgrade
];
}
@@ -119,6 +122,95 @@ if (isset($criterias['sn']) && $criterias['sn'] != ''){
exit;
}
// Check if sw_version_upgrade is set - this overrides normal availability check
if (!empty($sw_version_upgrade)) {
if (debug) {
$debug['sw_version_upgrade_check'] = [
'sw_version_upgrade_id' => $sw_version_upgrade,
'checking_override' => true
];
}
// Check if this version exists and is active
$sql = 'SELECT
psv.rowID as version_id,
psv.version,
psv.name,
psv.description,
psv.mandatory,
psv.latest,
psv.hw_version,
psv.file_path,
psv.status
FROM products_software_versions psv
WHERE psv.rowID = ?';
$stmt = $pdo->prepare($sql);
$stmt->execute([$sw_version_upgrade]);
$upgrade_version = $stmt->fetch(PDO::FETCH_ASSOC);
if ($upgrade_version && $upgrade_version['status'] == 1) {
// Valid override found - check if different from current version
$normalized_upgrade_version = strtolower(ltrim($upgrade_version['version'], '0'));
if (debug) {
$debug['sw_version_upgrade_check']['found_version'] = [
'version' => $upgrade_version['version'],
'name' => $upgrade_version['name'],
'normalized' => $normalized_upgrade_version,
'status' => $upgrade_version['status'],
'is_different_from_current' => ($current_sw_version != $normalized_upgrade_version)
];
}
if (!$current_sw_version || $current_sw_version == '' || $normalized_upgrade_version != $current_sw_version) {
// Override version is different from current (or no current) - return only this upgrade
$output[] = [
"productcode" => $productcode,
"name" => $upgrade_version['name'] ?? '',
"version" => $upgrade_version['version'],
"version_id" => $upgrade_version['version_id'],
"description" => $upgrade_version['description'] ?? '',
"hw_version" => $upgrade_version['hw_version'] ?? '',
"mandatory" => $upgrade_version['mandatory'] ?? '',
"latest" => $upgrade_version['latest'] ?? '',
"software" => $upgrade_version['file_path'] ?? '',
"source" => '',
"source_type" => '',
"price" => '0.00',
"currency" => '',
"is_current" => false
];
// Generate download token
$download_token = create_download_url_token($criterias['sn'], $upgrade_version['version_id']);
$download_url = 'https://'.$_SERVER['SERVER_NAME'].'/api.php/v2/software_download?token='.$download_token;
$output[0]['source'] = $download_url;
$output[0]['source_type'] = 'token_url';
if (debug) {
$debug['sw_version_upgrade_check']['decision'] = 'Override version returned as only upgrade';
$output[0]['_debug'] = $debug;
}
} else {
// Override version is same as current - no upgrades
if (debug) {
$debug['sw_version_upgrade_check']['decision'] = 'Override version is same as current version - no upgrades';
$output = ['message' => 'No upgrades available', 'debug' => $debug];
}
}
$messages = $output;
echo json_encode($messages, JSON_UNESCAPED_UNICODE);
exit;
} else {
// Override version not found or inactive - fall back to standard check
if (debug) {
$debug['sw_version_upgrade_check']['found_version'] = $upgrade_version ? 'found but inactive' : 'not found';
$debug['sw_version_upgrade_check']['decision'] = 'Falling back to standard check';
}
}
}
//GET ALL ACTIVE SOFTWARE ASSIGNMENTS for this product with matching HW version
$sql = 'SELECT
psv.rowID as version_id,

View File

@@ -0,0 +1,93 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// Marketing Files Delete
//------------------------------------------
//Connect to DB
$pdo = dbConnect($dbname);
//CONTENT FROM API (POST)
$post_content = json_decode($input,true);
//SoldTo is empty
if (empty($partner->soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';}
//default whereclause
list($whereclause,$condition) = getWhereclauselvl2("",$permission,$partner,'');
$file_id = $post_content['file_id'] ?? '';
if (empty($file_id)) {
echo json_encode(['error' => 'File ID is required']);
exit;
}
//QUERY AND VERIFY ALLOWED
if (isAllowed('marketing',$profile,$permission,'D') === 1){
// Get file information for cleanup
$file_sql = 'SELECT * FROM marketing_files WHERE id = ? AND accounthierarchy LIKE ?';
$stmt = $pdo->prepare($file_sql);
$stmt->execute([$file_id, '%' . $partner->soldto . '%']);
$file_info = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$file_info) {
echo json_encode(['error' => 'File not found or access denied']);
exit;
}
try {
$pdo->beginTransaction();
// Remove file tags
$delete_tags_sql = 'DELETE FROM marketing_file_tags WHERE file_id = ?';
$stmt = $pdo->prepare($delete_tags_sql);
$stmt->execute([$file_id]);
// Delete file record
$delete_file_sql = 'DELETE FROM marketing_files WHERE id = ? AND accounthierarchy LIKE ?';
$stmt = $pdo->prepare($delete_file_sql);
$stmt->execute([$file_id, '%' . $partner->soldto . '%']);
// Delete physical files
$base_path = dirname(__FILE__, 4) . "/";
$main_file = $base_path . $file_info['file_path'];
$thumbnail_file = $file_info['thumbnail_path'] ? $base_path . $file_info['thumbnail_path'] : null;
$files_deleted = [];
$files_failed = [];
if (file_exists($main_file)) {
if (unlink($main_file)) {
$files_deleted[] = $file_info['file_path'];
} else {
$files_failed[] = $file_info['file_path'];
}
}
if ($thumbnail_file && file_exists($thumbnail_file)) {
if (unlink($thumbnail_file)) {
$files_deleted[] = $file_info['thumbnail_path'];
} else {
$files_failed[] = $file_info['thumbnail_path'];
}
}
$pdo->commit();
echo json_encode([
'success' => true,
'message' => 'File deleted successfully',
'files_deleted' => $files_deleted,
'files_failed' => $files_failed
]);
} catch (Exception $e) {
$pdo->rollback();
echo json_encode(['error' => 'Failed to delete file: ' . $e->getMessage()]);
}
} else {
echo json_encode(['error' => 'Insufficient permissions']);
}
?>

View File

@@ -0,0 +1,105 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// Marketing Folders
//------------------------------------------
//Connect to DB
$pdo = dbConnect($dbname);
//CONTENT FROM API (POST)
$post_content = json_decode($input,true);
//SoldTo is empty
if (empty($partner->soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';}
//default whereclause
list($whereclause,$condition) = getWhereclauselvl2("",$permission,$partner,'');
//BUILD UP PARTNERHIERARCHY FROM USER
$partner_hierarchy = json_encode(array("salesid"=>$partner->salesid,"soldto"=>$partner->soldto), JSON_UNESCAPED_UNICODE);
$id = $post_content['id'] ?? ''; //check for rowID
$command = ($id == '')? 'insert' : 'update'; //IF rowID = empty then INSERT
if (isset($post_content['delete'])){$command = 'delete';} //change command to delete
$date = date('Y-m-d H:i:s');
//CREATE EMPTY STRINGS
$clause = '';
$clause_insert ='';
$input_insert = '';
if ($command == 'update'){
$post_content['updatedby'] = $username;
$post_content['updated'] = $date;
}
if ($command == 'insert'){
$post_content['createdby'] = $username;
$post_content['accounthierarchy'] = $partner_hierarchy;
}
//CREATE NEW ARRAY AND MAP TO CLAUSE
if(isset($post_content) && $post_content!=''){
foreach ($post_content as $key => $var){
if ($key == 'submit' || $key == 'id' || $key == 'delete'){
//do nothing
}
else {
// Handle empty parent_id as NULL for foreign key constraint
if ($key == 'parent_id' && $var === '') {
$var = null;
}
$criterias[$key] = $var;
$clause .= ' , '.$key.' = ?';
$clause_insert .= ' , '.$key.'';
$input_insert .= ', ?'; // ? for each insert item
$execute_input[]= $var; // Build array for input
}
}
}
//CLEAN UP INPUT
$clause = substr($clause, 2); //Clean clause - remove first comma
$clause_insert = substr($clause_insert, 2); //Clean clause - remove first comma
$input_insert = substr($input_insert, 1); //Clean clause - remove first comma
//QUERY AND VERIFY ALLOWED
if ($command == 'update' && isAllowed('marketing',$profile,$permission,'U') === 1){
$sql = 'UPDATE marketing_folders SET '.$clause.' WHERE id = ? '.$whereclause.'';
$execute_input[] = $id;
$stmt = $pdo->prepare($sql);
$stmt->execute($execute_input);
echo json_encode(['success' => true, 'message' => 'Folder updated successfully']);
}
elseif ($command == 'insert' && isAllowed('marketing',$profile,$permission,'C') === 1){
$sql = 'INSERT INTO marketing_folders ('.$clause_insert.') VALUES ('.$input_insert.')';
$stmt = $pdo->prepare($sql);
$stmt->execute($execute_input);
$folder_id = $pdo->lastInsertId();
echo json_encode(['success' => true, 'rowID' => $folder_id, 'message' => 'Folder created successfully']);
}
elseif ($command == 'delete' && isAllowed('marketing',$profile,$permission,'D') === 1){
// Check if folder has subfolders
$subfolder_sql = 'SELECT COUNT(*) as count FROM marketing_folders WHERE parent_id = ? AND accounthierarchy LIKE ?';
$stmt = $pdo->prepare($subfolder_sql);
$stmt->execute([$id, '%' . $partner->soldto . '%']);
$subfolder_count = $stmt->fetch()['count'];
// Check if folder has files
$files_sql = 'SELECT COUNT(*) as count FROM marketing_files WHERE folder_id = ? AND accounthierarchy LIKE ?';
$stmt = $pdo->prepare($files_sql);
$stmt->execute([$id, '%' . $partner->soldto . '%']);
$files_count = $stmt->fetch()['count'];
if ($subfolder_count > 0 || $files_count > 0) {
echo json_encode(['error' => 'Cannot delete folder that contains subfolders or files']);
} else {
$stmt = $pdo->prepare('DELETE FROM marketing_folders WHERE id = ? '.$whereclause.'');
$stmt->execute([ $id ]);
echo json_encode(['success' => true, 'message' => 'Folder deleted successfully']);
}
} else {
echo json_encode(['error' => 'Insufficient permissions or invalid operation']);
}
?>

View File

@@ -0,0 +1,302 @@
<?php
defined($security_key) or exit;
//------------------------------------------
// Marketing Upload
//------------------------------------------
//Connect to DB
$pdo = dbConnect($dbname);
//SoldTo is empty
if (empty($partner->soldto) || $partner->soldto == ''){$soldto_search = '%';} else {$soldto_search = '-%';}
//default whereclause
list($whereclause,$condition) = getWhereclauselvl2("",$permission,$partner,'');
//BUILD UP PARTNERHIERARCHY FROM USER
$partner_hierarchy = $condition;
//QUERY AND VERIFY ALLOWED
if (isAllowed('marketing',$profile,$permission,'C') === 1){
if (!isset($_FILES['file'])) {
echo json_encode(['success' => false, 'error' => 'No file uploaded']);
exit;
}
$file = $_FILES['file'];
$folder_id = $_POST['folder_id'] ?? '';
$tags = isset($_POST['tags']) ? json_decode($_POST['tags'], true) : [];
$title = $_POST['title'] ?? pathinfo($file['name'], PATHINFO_FILENAME);
// Validate file type
$allowedTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'mp4', 'mov', 'avi'];
$filename = $file['name'];
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!in_array($ext, $allowedTypes)) {
echo json_encode(['success' => false, 'error' => 'Invalid file type. Allowed: ' . implode(', ', $allowedTypes)]);
exit;
}
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$isImage = in_array($ext, $imageTypes);
// For images over 10MB, automatically compress
if ($isImage && $file['size'] > 10000000) {
$compressed = compressImage($file['tmp_name'], $ext, 10000000);
if ($compressed === false) {
echo json_encode(['success' => false, 'error' => 'Failed to compress large image. Please reduce file size manually.']);
exit;
}
// Update file size after compression
$file['size'] = filesize($file['tmp_name']);
}
// Non-images must be under 10MB
if (!$isImage && $file['size'] > 10000000) {
echo json_encode(['success' => false, 'error' => 'File too large. Maximum size is 10MB.']);
exit;
}
// Create unique filename
$unique_filename = uniqid() . '_' . time() . '.' . $ext;
$target_dir = dirname(__FILE__, 4) . "/marketing/uploads/";
$target_file = $target_dir . $unique_filename;
$logical_path = "marketing/uploads/" . $unique_filename;
// Ensure upload directory exists
if (!file_exists($target_dir)) {
mkdir($target_dir, 0755, true);
}
if (move_uploaded_file($file['tmp_name'], $target_file)) {
// Generate thumbnail for images
$thumbnail_path = null;
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) {
$thumb_dir = $target_dir . "thumbs/";
if (!file_exists($thumb_dir)) {
mkdir($thumb_dir, 0755, true);
}
$thumbnail_file = $thumb_dir . $unique_filename;
if (generateThumbnail($target_file, $thumbnail_file, 200, 200)) {
$thumbnail_path = "marketing/uploads/thumbs/" . $unique_filename;
}
}
// Insert into database
$insert_sql = 'INSERT INTO `marketing_files` (`title`, `original_filename`, `file_path`, `thumbnail_path`, `file_type`, `file_size`, `folder_id`, `tags`, `createdby`, `accounthierarchy`) VALUES (?,?,?,?,?,?,?,?,?,?)';
$stmt = $pdo->prepare($insert_sql);
$stmt->execute([
$title,
$filename,
$logical_path,
$thumbnail_path,
$ext,
$file['size'],
$folder_id,
json_encode($tags),
$username,
$partner_hierarchy
]);
$file_id = $pdo->lastInsertId();
// Insert tags into separate table
if (!empty($tags)) {
$tag_sql = 'INSERT IGNORE INTO `marketing_tags` (`tag_name`) VALUES (?)';
$tag_stmt = $pdo->prepare($tag_sql);
$file_tag_sql = 'INSERT INTO `marketing_file_tags` (`file_id`, `tag_id`) SELECT ?, id FROM marketing_tags WHERE tag_name = ?';
$file_tag_stmt = $pdo->prepare($file_tag_sql);
foreach ($tags as $tag) {
$tag_stmt->execute([trim($tag)]);
$file_tag_stmt->execute([$file_id, trim($tag)]);
}
}
echo json_encode([
'success' => true,
'file_id' => $file_id,
'path' => $logical_path,
'thumbnail' => $thumbnail_path,
'message' => 'File uploaded successfully'
]);
} else {
echo json_encode(['success' => false, 'error' => 'Failed to move uploaded file']);
}
} else {
echo json_encode(['success' => false, 'error' => 'Insufficient permissions']);
}
// Function to compress large images
function compressImage($source, $ext, $maxSize) {
$info = @getimagesize($source);
if ($info === false) return false;
$mime = $info['mime'];
// Load image
switch ($mime) {
case 'image/jpeg':
$image = @imagecreatefromjpeg($source);
break;
case 'image/png':
$image = @imagecreatefrompng($source);
break;
case 'image/gif':
$image = @imagecreatefromgif($source);
break;
case 'image/webp':
$image = @imagecreatefromwebp($source);
break;
default:
return false;
}
if ($image === false) return false;
$width = imagesx($image);
$height = imagesy($image);
// Start with 90% quality and reduce dimensions if needed
$quality = 90;
$scale = 1.0;
$tempFile = $source . '.tmp';
// Try progressive compression
while (true) {
// Calculate new dimensions
$newWidth = (int)($width * $scale);
$newHeight = (int)($height * $scale);
// Create resized image
$resized = imagecreatetruecolor($newWidth, $newHeight);
// Preserve transparency for PNG/GIF
if ($mime === 'image/png' || $mime === 'image/gif') {
imagealphablending($resized, false);
imagesavealpha($resized, true);
$transparent = imagecolorallocatealpha($resized, 255, 255, 255, 127);
imagefilledrectangle($resized, 0, 0, $newWidth, $newHeight, $transparent);
}
imagecopyresampled($resized, $image, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
// Save with current quality
if ($ext === 'jpg' || $ext === 'jpeg') {
imagejpeg($resized, $tempFile, $quality);
} elseif ($ext === 'png') {
// PNG compression level (0-9, where 9 is best compression)
$pngQuality = (int)((100 - $quality) / 11);
imagepng($resized, $tempFile, $pngQuality);
} elseif ($ext === 'webp') {
imagewebp($resized, $tempFile, $quality);
} else {
imagegif($resized, $tempFile);
}
imagedestroy($resized);
$fileSize = filesize($tempFile);
// If file is small enough, use it
if ($fileSize <= $maxSize) {
imagedestroy($image);
rename($tempFile, $source);
return true;
}
// If we've reduced too much, give up
if ($quality < 50 && $scale < 0.5) {
imagedestroy($image);
@unlink($tempFile);
return false;
}
// Reduce quality or scale
if ($quality > 50) {
$quality -= 10;
} else {
$scale -= 0.1;
}
}
}
// Function to generate thumbnail
function generateThumbnail($source, $destination, $width, $height) {
$info = getimagesize($source);
if ($info === false) return false;
$mime = $info['mime'];
switch ($mime) {
case 'image/jpeg':
$image = imagecreatefromjpeg($source);
break;
case 'image/png':
$image = imagecreatefrompng($source);
break;
case 'image/gif':
$image = imagecreatefromgif($source);
break;
case 'image/webp':
$image = imagecreatefromwebp($source);
break;
default:
return false;
}
if ($image === false) return false;
$original_width = imagesx($image);
$original_height = imagesy($image);
// Calculate aspect ratio
$aspect_ratio = $original_width / $original_height;
if ($width / $height > $aspect_ratio) {
$new_width = $height * $aspect_ratio;
$new_height = $height;
} else {
$new_height = $width / $aspect_ratio;
$new_width = $width;
}
$thumbnail = imagecreatetruecolor($new_width, $new_height);
// Preserve transparency
imagealphablending($thumbnail, false);
imagesavealpha($thumbnail, true);
$transparent = imagecolorallocatealpha($thumbnail, 255, 255, 255, 127);
imagefilledrectangle($thumbnail, 0, 0, $new_width, $new_height, $transparent);
imagecopyresampled($thumbnail, $image, 0, 0, 0, 0, $new_width, $new_height, $original_width, $original_height);
// Save thumbnail
switch ($mime) {
case 'image/jpeg':
$result = imagejpeg($thumbnail, $destination, 85);
break;
case 'image/png':
$result = imagepng($thumbnail, $destination, 8);
break;
case 'image/gif':
$result = imagegif($thumbnail, $destination);
break;
case 'image/webp':
$result = imagewebp($thumbnail, $destination, 85);
break;
default:
$result = false;
}
imagedestroy($image);
imagedestroy($thumbnail);
return $result;
}
?>

View File

@@ -6,7 +6,7 @@ defined($security_key) or exit;
//------------------------------------------
// Payment Creation (for Software Upgrades)
//------------------------------------------
// This endpoint creates a Mollie payment and stores transaction data
// This endpoint creates a payment (Mollie or PayPal) and stores transaction data
//Connect to DB
$pdo = dbConnect($dbname);
@@ -25,6 +25,8 @@ if (empty($post_content['serial_number']) || empty($post_content['version_id']))
$serial_number = $post_content['serial_number'];
$version_id = $post_content['version_id'];
$user_data = $post_content['user_data'] ?? [];
// Read payment_provider from top level first, then fallback to user_data
$payment_provider = $post_content['payment_provider'] ?? $user_data['payment_provider'] ?? 'mollie';
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// STEP 1: Get equipment data from serial_number
@@ -137,67 +139,150 @@ if ($final_price <= 0) {
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// STEP 6: DEBUG MODE - Log but continue to real Mollie
// STEP 6: DEBUG MODE - Log
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
if (debug) {
debuglog("DEBUG MODE: Creating real Mollie payment for testing");
debuglog("DEBUG MODE: Creating $payment_provider payment for testing");
debuglog("DEBUG: Serial Number: $serial_number, Version ID: $version_id, Price: $final_price");
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// STEP 7: Call Mollie API to create payment
// STEP 7: Create payment based on provider
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
try {
// Initialize Mollie
require dirname(__FILE__, 4).'/initialize.php';
// Format price for Mollie (must be string with 2 decimals)
// Format price (must be string with 2 decimals)
$formatted_price = number_format((float)$final_price, 2, '.', '');
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// STEP 7A: Generate transaction ID BEFORE creating Mollie payment
// STEP 7A: Generate transaction ID BEFORE creating payment
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Generate unique transaction ID (same as placeorder.php)
$txn_id = strtoupper(uniqid('SC') . substr(md5(mt_rand()), 0, 5));
// Build webhook URL and redirect URL with actual transaction ID
// Build URLs
$protocol = 'https';
$hostname = $_SERVER['SERVER_NAME'];
$path = '/';
$webhook_url = "{$protocol}://{$hostname}{$path}webhook_mollie.php";
$redirect_url = "{$protocol}://{$hostname}{$path}?page=softwaretool&payment_return=1&order_id={$txn_id}";
if (debug) {
debuglog("DEBUG: Transaction ID: {$txn_id}");
debuglog("DEBUG: redirectUrl being sent to Mollie: " . $redirect_url);
debuglog("DEBUG: Redirect URL: " . $redirect_url);
}
// Create payment with Mollie
$payment = $mollie->payments->create([
'amount' => [
'currency' => $final_currency ?: 'EUR',
'value' => "{$formatted_price}"
],
'description' => "Software upgrade Order #{$txn_id}",
'redirectUrl' => "{$redirect_url}",
'webhookUrl' => "{$webhook_url}",
'metadata' => [
'order_id' => $txn_id,
'serial_number' => $serial_number,
'version_id' => $version_id,
'equipment_id' => $equipment_id
]
]);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Create payment based on selected provider
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
if ($payment_provider === 'paypal') {
//==========================================
// PAYPAL PAYMENT CREATION
//==========================================
$cancel_url = "{$protocol}://{$hostname}{$path}?page=softwaretool&payment_return=cancelled&order_id={$txn_id}";
// Get PayPal access token
$access_token = getPayPalAccessToken();
$mollie_payment_id = $payment->id;
$checkout_url = $payment->getCheckoutUrl();
// Create PayPal order
$order_data = [
'intent' => 'CAPTURE',
'purchase_units' => [[
'custom_id' => $txn_id,
'description' => "Software upgrade Order #{$txn_id}",
'amount' => [
'currency_code' => $final_currency ?: 'EUR',
'value' => $formatted_price
],
'payee' => [
'email_address' => email
]
]],
'application_context' => [
'return_url' => $redirect_url,
'cancel_url' => $cancel_url,
'brand_name' => site_name,
'user_action' => 'PAY_NOW'
]
];
$ch = curl_init(PAYPAL_URL . '/v2/checkout/orders');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($order_data));
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 ($http_code != 200 && $http_code != 201) {
debuglog("PayPal API Error: HTTP $http_code - Response: $response");
throw new Exception("PayPal order creation failed: HTTP $http_code");
}
$paypal_order = json_decode($response, true);
$payment_id = $paypal_order['id'] ?? null;
// Extract approval URL
$checkout_url = '';
foreach ($paypal_order['links'] ?? [] as $link) {
if ($link['rel'] === 'approve') {
$checkout_url = $link['href'];
break;
}
}
if (!$checkout_url) {
throw new Exception("No approval URL received from PayPal");
}
$payment_method_id = 1; // PayPal
$payment_metadata = 'paypal_order_id';
} else {
//==========================================
// MOLLIE PAYMENT CREATION
//==========================================
// Initialize Mollie
require dirname(__FILE__, 4).'/initialize.php';
$webhook_url = "{$protocol}://{$hostname}{$path}webhook_mollie.php";
// Create payment with Mollie
$payment = $mollie->payments->create([
'amount' => [
'currency' => $final_currency ?: 'EUR',
'value' => "{$formatted_price}"
],
'description' => "Software upgrade Order #{$txn_id}",
'redirectUrl' => "{$redirect_url}",
'webhookUrl' => "{$webhook_url}",
'metadata' => [
'order_id' => $txn_id,
'serial_number' => $serial_number,
'version_id' => $version_id,
'equipment_id' => $equipment_id
]
]);
$payment_id = $payment->id;
$checkout_url = $payment->getCheckoutUrl();
if (debug) {
debuglog("DEBUG: Mollie payment created successfully");
debuglog("DEBUG: Payment ID: $payment_id");
debuglog("DEBUG: Redirect URL sent: $redirect_url");
debuglog("DEBUG: Checkout URL: $checkout_url");
}
$payment_method_id = 0; // Mollie
$payment_metadata = 'mollie_payment_id';
}
if (debug) {
debuglog("DEBUG: Mollie payment created successfully");
debuglog("DEBUG: Payment ID: $mollie_payment_id");
debuglog("DEBUG: Redirect URL sent: $redirect_url");
debuglog("DEBUG: Redirect URL from Mollie object: " . $payment->redirectUrl);
debuglog("DEBUG: Full payment object: " . json_encode($payment));
debuglog("DEBUG: Payment created via $payment_provider");
debuglog("DEBUG: Payment ID: $payment_id");
debuglog("DEBUG: Checkout URL: $checkout_url");
}
@@ -218,7 +303,7 @@ try {
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
$stmt = $pdo->prepare($sql);
$stmt->execute([
$txn_id, // Use generated transaction ID, not Mollie payment ID
$txn_id,
$final_price,
0, // 0 = pending
$user_data['email'] ?? '',
@@ -230,7 +315,7 @@ try {
$user_data['postal'] ?? '',
$user_data['country'] ?? '',
$serial_number,
0, // payment method
$payment_method_id, // 0 = Mollie, 1 = PayPal
$partner_product,
date('Y-m-d H:i:s')
]);
@@ -245,14 +330,14 @@ try {
'serial_number' => $serial_number,
'equipment_id' => $equipment_id,
'hw_version' => $hw_version,
'mollie_payment_id' => $mollie_payment_id // Store Mollie payment ID in options
$payment_metadata => $payment_id // Store payment provider ID
], JSON_UNESCAPED_UNICODE);
$sql = 'INSERT INTO transactions_items (txn_id, item_id, item_price, item_quantity, item_options, created)
VALUES (?, ?, ?, ?, ?, ?)';
$stmt = $pdo->prepare($sql);
$stmt->execute([
$transaction_id, // Use database transaction ID (not txn_id string, not mollie_payment_id)
$transaction_id,
$version_id,
$final_price,
1,
@@ -265,7 +350,7 @@ try {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
$messages = json_encode([
'checkout_url' => $checkout_url,
'payment_id' => $mollie_payment_id
'payment_id' => $payment_id
], JSON_UNESCAPED_UNICODE);
echo $messages;
@@ -275,4 +360,27 @@ try {
exit;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// Helper function to get PayPal access token
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
function getPayPalAccessToken() {
$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);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code != 200) {
throw new Exception("Failed to get PayPal access token: HTTP $http_code");
}
$result = json_decode($response, true);
return $result['access_token'] ?? '';
}
?>