- 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.
387 lines
14 KiB
PHP
387 lines
14 KiB
PHP
<?php
|
|
defined($security_key) or exit;
|
|
ini_set('display_errors', '1');
|
|
ini_set('display_startup_errors', '1');
|
|
error_reporting(E_ALL);
|
|
//------------------------------------------
|
|
// Payment Creation (for Software Upgrades)
|
|
//------------------------------------------
|
|
// This endpoint creates a payment (Mollie or PayPal) and stores transaction data
|
|
|
|
//Connect to DB
|
|
$pdo = dbConnect($dbname);
|
|
|
|
//CONTENT FROM API (POST)
|
|
$post_content = json_decode($input, true);
|
|
|
|
|
|
// Validate required inputs
|
|
if (empty($post_content['serial_number']) || empty($post_content['version_id'])) {
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'Missing required fields: serial_number, version_id'], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
$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
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
$sql = 'SELECT rowID, sw_version, sw_version_license, hw_version FROM equipment WHERE serialnumber = ?';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([$serial_number]);
|
|
$equipment = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$equipment) {
|
|
|
|
http_response_code(404);
|
|
echo json_encode(['error' => 'Device not found with serial number: ' . $serial_number], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
$equipment_id = $equipment['rowID'];
|
|
$current_sw_version = trim(strtolower(ltrim($equipment['sw_version'], '0')));
|
|
$sw_version_license = $equipment['sw_version_license'] ?? null;
|
|
$hw_version = $equipment['hw_version'] ?? '';
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 2: Get version data from version_id
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
$sql = 'SELECT rowID as version_id, version, name, description, hw_version
|
|
FROM products_software_versions
|
|
WHERE rowID = ? AND status = 1';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([$version_id]);
|
|
$version = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$version) {
|
|
|
|
http_response_code(404);
|
|
echo json_encode(['error' => 'Software version not found or inactive'], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 3: Calculate price SERVER-SIDE (same logic as software_update.php)
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
$final_price = '0.00';
|
|
$final_currency = '';
|
|
|
|
// Check if version has upgrade paths defined
|
|
$sql = 'SELECT COUNT(*) as path_count FROM products_software_upgrade_paths WHERE to_version_id = ? AND is_active = 1';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([$version_id]);
|
|
$path_count_result = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
$has_upgrade_paths = ($path_count_result['path_count'] > 0);
|
|
|
|
if (!$has_upgrade_paths) {
|
|
// No upgrade paths defined = FREE (lines 240-242 in software_update.php)
|
|
$final_price = '0.00';
|
|
} else {
|
|
// Check for valid upgrade path FROM current version
|
|
$sql = 'SELECT pup.price, pup.currency
|
|
FROM products_software_upgrade_paths pup
|
|
JOIN products_software_versions from_ver ON pup.from_version_id = from_ver.rowID
|
|
WHERE pup.to_version_id = ?
|
|
AND LOWER(TRIM(LEADING "0" FROM from_ver.version)) = ?
|
|
AND pup.is_active = 1';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([$version_id, $current_sw_version]);
|
|
$upgrade_path = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($upgrade_path) {
|
|
$final_price = $upgrade_path['price'] ?? '0.00';
|
|
$final_currency = $upgrade_path['currency'] ?? 'EUR';
|
|
} else {
|
|
// No upgrade path FROM current version
|
|
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'No valid upgrade path from current version'], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 4: Check license validity (lines 280-311 in software_update.php)
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
if ($final_price > 0 && $sw_version_license) {
|
|
$sql = 'SELECT status, starts_at, expires_at
|
|
FROM products_software_licenses
|
|
WHERE license_key = ?';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([$sw_version_license]);
|
|
$license = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($license && $license['status'] == 1) {
|
|
$now = date('Y-m-d H:i:s');
|
|
$starts_at = $license['starts_at'];
|
|
$expires_at = $license['expires_at'];
|
|
|
|
// Check if license is within valid date range
|
|
if ((!$starts_at || $starts_at <= $now) && (!$expires_at || $expires_at >= $now)) {
|
|
$final_price = '0.00';
|
|
}
|
|
}
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 5: Verify price > 0 (free upgrades shouldn't reach payment API)
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
if ($final_price <= 0) {
|
|
|
|
http_response_code(400);
|
|
echo json_encode(['error' => 'This upgrade is free. No payment required.'], JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 6: DEBUG MODE - Log
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
if (debug) {
|
|
debuglog("DEBUG MODE: Creating $payment_provider payment for testing");
|
|
debuglog("DEBUG: Serial Number: $serial_number, Version ID: $version_id, Price: $final_price");
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 7: Create payment based on provider
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
try {
|
|
// Format price (must be string with 2 decimals)
|
|
$formatted_price = number_format((float)$final_price, 2, '.', '');
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 7A: Generate transaction ID BEFORE creating payment
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
$txn_id = strtoupper(uniqid('SC') . substr(md5(mt_rand()), 0, 5));
|
|
|
|
// Build URLs
|
|
$protocol = 'https';
|
|
$hostname = $_SERVER['SERVER_NAME'];
|
|
$path = '/';
|
|
$redirect_url = "{$protocol}://{$hostname}{$path}?page=softwaretool&payment_return=1&order_id={$txn_id}";
|
|
|
|
if (debug) {
|
|
debuglog("DEBUG: Transaction ID: {$txn_id}");
|
|
debuglog("DEBUG: Redirect URL: " . $redirect_url);
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// 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();
|
|
|
|
// 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: Payment created via $payment_provider");
|
|
debuglog("DEBUG: Payment ID: $payment_id");
|
|
debuglog("DEBUG: Checkout URL: $checkout_url");
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 8: Store transaction in DB using txn_id (order ID)
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// Split name into first/last (simple split on first space)
|
|
$full_name = $user_data['name'] ?? '';
|
|
$name_parts = explode(' ', $full_name, 2);
|
|
$first_name = $name_parts[0] ?? '';
|
|
$last_name = $name_parts[1] ?? '';
|
|
|
|
// BUILD UP PARTNERHIERARCHY FROM USER
|
|
$partner_product = json_encode(array("salesid"=>$partner->salesid,"soldto"=>$partner->soldto), JSON_UNESCAPED_UNICODE);
|
|
|
|
$sql = 'INSERT INTO transactions (txn_id, payment_amount, payment_status, payer_email, first_name, last_name,
|
|
address_street, address_city, address_state, address_zip, address_country, account_id, payment_method, accounthierarchy, created)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([
|
|
$txn_id,
|
|
$final_price,
|
|
0, // 0 = pending
|
|
$user_data['email'] ?? '',
|
|
$first_name,
|
|
$last_name,
|
|
$user_data['address'] ?? '',
|
|
$user_data['city'] ?? '',
|
|
'', // address_state (not collected)
|
|
$user_data['postal'] ?? '',
|
|
$user_data['country'] ?? '',
|
|
$serial_number,
|
|
$payment_method_id, // 0 = Mollie, 1 = PayPal
|
|
$partner_product,
|
|
date('Y-m-d H:i:s')
|
|
]);
|
|
|
|
// Get the database ID
|
|
$transaction_id = $pdo->lastInsertId();
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 9: Store transaction item with serial_number in item_options
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
$item_options = json_encode([
|
|
'serial_number' => $serial_number,
|
|
'equipment_id' => $equipment_id,
|
|
'hw_version' => $hw_version,
|
|
$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,
|
|
$version_id,
|
|
$final_price,
|
|
1,
|
|
$item_options,
|
|
date('Y-m-d H:i:s')
|
|
]);
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// STEP 10: Return checkout URL and payment ID
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
$messages = json_encode([
|
|
'checkout_url' => $checkout_url,
|
|
'payment_id' => $payment_id
|
|
], JSON_UNESCAPED_UNICODE);
|
|
echo $messages;
|
|
|
|
} catch (Exception $e) {
|
|
http_response_code(500);
|
|
echo json_encode(['error' => 'Payment creation failed: ' . $e->getMessage()], JSON_UNESCAPED_UNICODE);
|
|
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'] ?? '';
|
|
}
|
|
|
|
?>
|