422 lines
16 KiB
PHP
422 lines
16 KiB
PHP
<?php
|
|
defined($security_key) or exit;
|
|
//------------------------------------------
|
|
// 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';
|
|
|
|
// Extract tax information from user_data (sent from frontend)
|
|
$item_price = $user_data['item_price'] ?? null; // Price without VAT
|
|
$tax_amount = $user_data['tax_amount'] ?? 0; // VAT amount
|
|
$payment_amount = $user_data['payment_amount'] ?? null; // Total including VAT
|
|
$vat_number = $user_data['vat_number'] ?? null; // VAT number
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// 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'];
|
|
// Normalize software version for comparison (lowercase, trim leading zeros) - same as software_update.php line 96
|
|
$current_sw_version = 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 328-331 in software_update.php)
|
|
$final_price = '0.00';
|
|
if (debug) {
|
|
debuglog("DEBUG: No upgrade paths defined for version_id $version_id - upgrade is FREE");
|
|
}
|
|
} else {
|
|
// Check for valid upgrade path FROM current version (same logic as software_update.php lines 335-353)
|
|
$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 (debug) {
|
|
debuglog("DEBUG: Looking for upgrade path TO version_id=$version_id FROM current_sw_version='$current_sw_version'");
|
|
debuglog("DEBUG: Upgrade path result: " . json_encode($upgrade_path));
|
|
}
|
|
|
|
if ($upgrade_path) {
|
|
$final_price = $upgrade_path['price'] ?? '0.00';
|
|
$final_currency = $upgrade_path['currency'] ?? 'EUR';
|
|
if (debug) {
|
|
debuglog("DEBUG: Found upgrade path - price: $final_price $final_currency");
|
|
}
|
|
} else {
|
|
// No upgrade path FROM current version
|
|
if (debug) {
|
|
debuglog("ERROR: No valid upgrade path from current version '$current_sw_version' to version_id $version_id");
|
|
}
|
|
http_response_code(400);
|
|
echo json_encode([
|
|
'error' => 'No valid upgrade path from current version',
|
|
'current_version' => $current_sw_version,
|
|
'target_version_id' => $version_id
|
|
], 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 {
|
|
// Use payment_amount (with tax) if provided, otherwise use final_price
|
|
$amount_to_charge = $payment_amount ? (float)$payment_amount : (float)$final_price;
|
|
|
|
// Format price (must be string with 2 decimals)
|
|
$formatted_price = number_format($amount_to_charge, 2, '.', '');
|
|
|
|
if (debug) {
|
|
debuglog("DEBUG: Item Price (excl. VAT): " . ($item_price ?? $final_price));
|
|
debuglog("DEBUG: Tax Amount: " . $tax_amount);
|
|
debuglog("DEBUG: Total Amount (incl. VAT): " . $amount_to_charge);
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// 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 = 3; // 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 = 1; // 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, tax_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, vat_number)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([
|
|
$txn_id,
|
|
$amount_to_charge, // Total amount including tax
|
|
$tax_amount, // Tax amount
|
|
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'),
|
|
$vat_number
|
|
]);
|
|
|
|
// 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);
|
|
|
|
// Use item_price (without VAT) if provided, otherwise use final_price
|
|
$item_price_to_store = $item_price ? (float)$item_price : (float)$final_price;
|
|
|
|
$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,
|
|
$item_price_to_store, // Price without VAT
|
|
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'] ?? '';
|
|
}
|
|
|
|
?>
|