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:
210
webhook_mollie.php
Normal file
210
webhook_mollie.php
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
// NEW FILE - Webhook for software upgrade payments
|
||||
// Based on structure from existing webhook.php from commerce product
|
||||
// Uses existing transaction API + invoice API + email system
|
||||
|
||||
require_once 'settings/config_redirector.php';
|
||||
require_once 'assets/functions.php';
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
//LOGIN TO API (same as commerce webhook.php)
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
$data = json_encode(array("clientID" => software_update_user, "clientsecret" => software_update_pw), JSON_UNESCAPED_UNICODE);
|
||||
$responses = ioAPIv2('/v2/authorization', $data,'');
|
||||
if (!empty($responses)){$responses = json_decode($responses,true);}else{$responses = '400';}
|
||||
$clientsecret = $responses['token'];
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// BASEURL is required for invoice template
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
$base_url = 'https://'.$_SERVER['SERVER_NAME'].'/';
|
||||
define('base_url', $base_url);
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// Initialize DomPDF for invoice generation
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
use Dompdf\Dompdf;
|
||||
use Dompdf\Options;
|
||||
$options = new Options();
|
||||
$options->set('isRemoteEnabled', true);
|
||||
$dompdf = new Dompdf($options);
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// Language mapping for invoices
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
$available_languages = [
|
||||
'NL' => 'NL',
|
||||
'BE' => 'NL',
|
||||
'US' => 'US',
|
||||
'GB' => 'US',
|
||||
'DE' => 'DE',
|
||||
'FR' => 'FR',
|
||||
'ES' => 'ES'
|
||||
];
|
||||
|
||||
try {
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// Initialize the Mollie API library
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
require "initialize.php"; // Mollie initialization
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// DEBUG MODE - Handle fake payment IDs
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
if (debug && isset($_POST["id"]) && strpos($_POST["id"], 'DEBUG_') === 0) {
|
||||
// In DEBUG mode, simulate webhook callback
|
||||
$payment_id = $_POST["id"];
|
||||
$orderId = $payment_id;
|
||||
|
||||
// Simulate payment status as paid
|
||||
$payment_status = 1; // Paid
|
||||
|
||||
} else {
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// PRODUCTION MODE - Retrieve the payment's current state from Mollie
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
$payment = $mollie->payments->get($_POST["id"]);
|
||||
$orderId = $payment->metadata->order_id;
|
||||
$payment_status = null;
|
||||
|
||||
if ($payment->isPaid() && !$payment->hasRefunds() && !$payment->hasChargebacks()) {
|
||||
$payment_status = 1; // Paid
|
||||
} elseif ($payment->isOpen() || $payment->isPending()) {
|
||||
$payment_status = 101; // Open/Pending
|
||||
} elseif ($payment->isFailed()) {
|
||||
$payment_status = 102; // Failed
|
||||
} elseif ($payment->isExpired()) {
|
||||
$payment_status = 103; // Expired
|
||||
} elseif ($payment->isCanceled()) {
|
||||
$payment_status = 999; // Canceled
|
||||
}
|
||||
}
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// Update transaction status via API
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
if ($payment_status !== null) {
|
||||
$payload = json_encode(array("txn_id" => $orderId, "payment_status" => $payment_status), JSON_UNESCAPED_UNICODE);
|
||||
$transaction = ioAPIv2('/v2/transactions/',$payload,$clientsecret);
|
||||
$transaction = json_decode($transaction,true);
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// Only create license and invoice if payment is PAID
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
if ($payment_status == 1 && $transaction !== null && !empty($transaction)) {
|
||||
if(count($transaction) > 0) {
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// CREATE LICENSE for software upgrade
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
$pdo = dbConnect($dbname);
|
||||
|
||||
// Fetch transaction items to find software upgrade
|
||||
$sql = 'SELECT * FROM transactions_items WHERE txn_id = ?';
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$orderId]);
|
||||
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (!empty($item['item_options'])) {
|
||||
$options = json_decode($item['item_options'], true);
|
||||
|
||||
// Check if this is a software upgrade (has serial_number and equipment_id)
|
||||
if (isset($options['serial_number']) && isset($options['equipment_id'])) {
|
||||
|
||||
// Check if license already exists for this transaction
|
||||
$sql = 'SELECT rowID FROM products_software_licenses WHERE transaction_id = ?';
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$orderId]);
|
||||
$existing_license = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$existing_license) {
|
||||
// Generate unique license key
|
||||
$license_key = generateUniqueLicenseKey();
|
||||
|
||||
// Create license
|
||||
$sql = 'INSERT INTO products_software_licenses
|
||||
(license_key, equipment_id, license_type, status, start_at, expires_at, transaction_id, created, createdby)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([
|
||||
$license_key,
|
||||
$options['equipment_id'],
|
||||
'upgrade',
|
||||
1, // active
|
||||
date('Y-m-d H:i:s'),
|
||||
'2099-12-31 23:59:59', // effectively permanent
|
||||
$orderId,
|
||||
date('Y-m-d H:i:s'),
|
||||
'webhook' // created by webhook
|
||||
]);
|
||||
|
||||
// Update equipment.sw_version_license
|
||||
$sql = 'UPDATE equipment SET sw_version_license = ? WHERE rowID = ?';
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$license_key, $options['equipment_id']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// Generate INVOICE via API
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
$payload = json_encode(array("txn_id" => $transaction['transaction_id']), JSON_UNESCAPED_UNICODE);
|
||||
$invoice = ioAPIv2('/v2/invoice/',$payload,$clientsecret);
|
||||
$invoice = json_decode($invoice,true);
|
||||
|
||||
if ($invoice !== null && !empty($invoice)) {
|
||||
// Fetch full invoice data with customer details
|
||||
$invoice_cust = ioAPIv2('/v2/invoice/list=invoice&id='.$invoice['invoice_id'],'',$clientsecret);
|
||||
$invoice_cust = json_decode($invoice_cust,true);
|
||||
|
||||
// Determine invoice language
|
||||
if (!empty($invoice_cust['customer']['language'])) {
|
||||
$invoice_language = strtoupper($invoice_cust['customer']['language']);
|
||||
} elseif (!empty($invoice_cust['customer']['country']) && isset($available_languages[strtoupper($invoice_cust['customer']['country'])])) {
|
||||
$invoice_language = $available_languages[strtoupper($invoice_cust['customer']['country'])];
|
||||
} else {
|
||||
$invoice_language = 'US'; // Default fallback
|
||||
}
|
||||
|
||||
// Generate invoice HTML (using custom template for software upgrades)
|
||||
list($data,$customer_email,$order_id) = generateSoftwareInvoice($invoice_cust,$orderId,$invoice_language);
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
//CREATE PDF using DomPDF
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
$dompdf->loadHtml($data);
|
||||
$dompdf->setPaper('A4', 'portrait');
|
||||
$dompdf->render();
|
||||
$subject = 'Software Upgrade - Invoice: '.$order_id;
|
||||
$attachment = $dompdf->output();
|
||||
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
//Send email via PHPMailer
|
||||
//+++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
send_mail($customer_email, $subject, $data, $attachment, $subject);
|
||||
|
||||
// Send to bookkeeping if configured
|
||||
if(invoice_bookkeeping){
|
||||
send_mail(email_bookkeeping, $subject, $data, $attachment, $subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return 200 OK to Mollie
|
||||
http_response_code(200);
|
||||
echo "OK";
|
||||
|
||||
} catch (\Mollie\Api\Exceptions\ApiException $e) {
|
||||
error_log("Webhook API call failed: " . htmlspecialchars($e->getMessage()));
|
||||
http_response_code(500);
|
||||
} catch (Exception $e) {
|
||||
error_log("Webhook error: " . htmlspecialchars($e->getMessage()));
|
||||
http_response_code(500);
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user