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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user