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:
93
api/v2/post/marketing_delete.php
Normal file
93
api/v2/post/marketing_delete.php
Normal 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']);
|
||||
}
|
||||
|
||||
?>
|
||||
105
api/v2/post/marketing_folders.php
Normal file
105
api/v2/post/marketing_folders.php
Normal 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']);
|
||||
}
|
||||
|
||||
?>
|
||||
302
api/v2/post/marketing_upload.php
Normal file
302
api/v2/post/marketing_upload.php
Normal 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;
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -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'] ?? '';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user