Add Mollie API integration and webhook for software upgrade payments

- Introduced the `CaBundle.php` class for managing CA certificates.
- Updated `installed.json` and `installed.php` to include the new `composer/ca-bundle` dependency.
- Added `platform_check.php` to enforce PHP version requirements.
- Created `initialize.php` for initializing the Mollie API client with the API key.
- Implemented `webhook_mollie.php` to handle webhook callbacks for software upgrade payments, including transaction status updates and invoice generation.
- Integrated DomPDF for generating invoices and sending them via email.
This commit is contained in:
“VeLiTi”
2025-12-21 14:44:37 +01:00
parent 653e33d7e9
commit 0f968aac14
159 changed files with 16197 additions and 21 deletions

View File

@@ -972,30 +972,44 @@ async function processPayment(paymentData, option, modal) {
try {
progressBar("10", "Processing payment...", "#04AA6D");
// TODO: Call payment provider API
// For now, simulate payment processing
await logCommunication(`Payment initiated: ${JSON.stringify(paymentData)}`, 'sent');
// SECURITY: Only send serial_number and version_id
// Server will calculate price to prevent tampering
const paymentRequest = {
serial_number: deviceSerialNumber,
version_id: option.version_id,
user_data: paymentData // name, email, address only
};
// Simulate API call to payment provider
// const paymentResponse = await fetch(link + '/v2/payment/process', {
// method: 'POST',
// headers: {
// 'Authorization': 'Bearer ' + document.getElementById("servicetoken").textContent,
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify(paymentData)
// });
await logCommunication(`Payment initiated for version ${option.version_id}`, 'sent');
// For now, simulate success after 2 seconds
await new Promise(resolve => setTimeout(resolve, 2000));
// Call payment API to create Mollie payment
const response = await fetch(link + "/v2/post/payment", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + document.getElementById("servicetoken").textContent
},
body: JSON.stringify(paymentRequest)
});
await logCommunication(`Payment successful`, 'received');
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "Failed to create payment");
}
// Close payment modal
document.body.removeChild(modal);
const result = await response.json();
// Proceed to download and install
await downloadAndInstallSoftware(option);
if (result.checkout_url) {
await logCommunication(`Redirecting to payment provider`, 'sent');
// Close modal before redirect
document.body.removeChild(modal);
// Redirect to Mollie checkout page
window.location.href = result.checkout_url;
} else {
throw new Error(result.error || "No checkout URL received");
}
} catch (error) {
await logCommunication(`Payment error: ${error.message}`, 'error');
@@ -1005,6 +1019,51 @@ async function processPayment(paymentData, option, modal) {
}
async function downloadAndInstallSoftware(option, customerData = null) {
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
// SECURITY: Check if returning from payment - verify serial number
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
const urlParams = new URLSearchParams(window.location.search);
const paymentId = urlParams.get('payment_id');
if (paymentId) {
try {
// Verify serial number matches payment
const response = await fetch(link + `/v2/get/payment?payment_id=${paymentId}`, {
method: "GET",
headers: {
"Authorization": "Bearer " + document.getElementById("servicetoken").textContent
}
});
if (response.ok) {
const paymentData = await response.json();
// Check if device serial number matches the one from payment
if (paymentData.serial_number && paymentData.serial_number !== deviceSerialNumber) {
const confirmed = confirm(
`WARNING: Different device detected!\n\n` +
`License was created for device: ${paymentData.serial_number}\n` +
`Currently connected device: ${deviceSerialNumber}\n\n` +
`The license is already applied to the original device. ` +
`Do you want to continue with this device anyway?`
);
if (!confirmed) {
progressBar("0", "Upload canceled by user", "#ff6666");
await logCommunication('Upload canceled - serial number mismatch', 'error');
return;
}
await logCommunication(`WARNING: Serial number mismatch detected (payment: ${paymentData.serial_number}, device: ${deviceSerialNumber})`, 'warning');
}
}
} catch (error) {
console.error("Error verifying payment:", error);
// Don't block if verification fails - log and continue
await logCommunication(`Payment verification failed: ${error.message}`, 'warning');
}
}
selectedSoftwareUrl = option.source;
if (!selectedSoftwareUrl) {