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

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'] ?? '';
}
?>