software-license-manager/woocommerce/includes/create-license-orders.php
Michel 3d6a5b3d10 Add new license renewal workflow and enhanced templates
Features:

Added new license renewal workflow to track historical orders and update associated_orders column for licenses.
Introduced improved handling for WooCommerce order IDs during license renewal.
Added new templates for license management, including renewal pages and license product display suggestions for empty carts.
Improvements:

Enhanced wc_slm_create_new_license to manage expiration dates, logging, and associated orders seamlessly.
Refactored license renewal logic to ensure existing orders are appended to the associated_orders column.
Improved database schema management for adding missing columns like associated_orders, ensuring backward compatibility and smoother upgrades.
Introduced structured retrieval of associated orders with the SLM_Utility::slm_get_associated_orders function to return well-formatted data.
Fixes:

Addressed metadata dependency by replacing _slm_lic_key usage with database lookups for better accuracy and reliability.
Fixed database version handling logic to ensure schema updates are properly applied during version upgrades.
Templates:

Added license cart template with suggested license products display for empty cart scenarios.
Included robust template structure to handle renewal messages and new license purchases dynamically.
Notes:

The new renewal workflow improves traceability by appending historical orders to the license's associated_orders.
Templates are designed to integrate seamlessly with WooCommerce workflows, providing a consistent user experience.
2024-11-25 15:03:33 -05:00

308 lines
13 KiB
PHP

<?php
/**
* SLM Plus WooCommerce Integration
* @package SLM Plus
* @since 4.5.5
*/
if (!defined('ABSPATH')) exit; // Exit if accessed directly
global $post, $woocommerce, $product;
$slm_options = get_option('slm_plugin_options'); // Retrieve plugin options
add_action('wp_ajax_slm_generate_licenses', 'slm_generate_licenses_callback');
function slm_generate_licenses_callback()
{
check_ajax_referer('slm_generate_licenses_nonce', 'security');
global $wpdb;
$response_data = ['html' => ''];
$success_count = 0;
$failure_count = 0;
$skipped_orders = [];
$skipped_reasons = [];
$generated_licenses = [];
// Retrieve Product ID and Subscription Type from the request
$default_product_id = isset($_POST['slm_product_id']) ? absint($_POST['slm_product_id']) : null;
$slm_lic_type = isset($_POST['subscription_type']) && in_array($_POST['subscription_type'], ['subscription', 'lifetime'])
? sanitize_text_field($_POST['subscription_type'])
: 'subscription';
//SLM_Helper_Class::write_log("Starting license generation with Product ID: {$default_product_id} and License Type: {$slm_lic_type}.");
// Check if Product ID is missing; if so, log an error, add an error response, and exit.
if (empty($default_product_id)) {
//SLM_Helper_Class::write_log('Error: Product ID is missing in the request.');
// Track failure and skip reason for the response
$failure_count++;
$skipped_orders[] = 0;
$skipped_reasons[0] = __('Product ID is missing in the request.', 'slm-plus');
// Return early with a JSON error response for AJAX display
$response_data['html'] .= '<li><strong>Error:</strong> Product ID is missing in the request. Please provide a valid product ID.</li>';
wp_send_json_error($response_data);
return;
}
// Check if the Product ID corresponds to an existing WooCommerce product
$product = wc_get_product($default_product_id);
if (!$product) {
//SLM_Helper_Class::write_log("Error: Product with ID $default_product_id does not exist in WooCommerce.");
// Track failure and skip reason for the response
$failure_count++;
$skipped_orders[] = 0;
$skipped_reasons[0] = __('The provided Product ID does not correspond to a valid WooCommerce product. Please check the ID and try again.', 'slm-plus');
// Return early with a JSON error response for AJAX display
$response_data['html'] .= '<li><strong>Error:</strong> The provided Product ID does not correspond to a valid WooCommerce product. Please check the ID and try again.</li>';
wp_send_json_error($response_data);
return;
}
// Query to get WooCommerce orders without a license key
$orders_without_license = $wpdb->get_results("
SELECT p.ID as order_id
FROM {$wpdb->prefix}posts p
LEFT JOIN {$wpdb->prefix}postmeta pm ON pm.post_id = p.ID AND pm.meta_key = '_license_key'
WHERE p.post_type = 'shop_order'
AND (pm.meta_value IS NULL OR pm.meta_value = '')
GROUP BY p.ID
");
foreach ($orders_without_license as $order_data) {
$order_id = $order_data->order_id;
$order = wc_get_order($order_id);
// Only proceed for orders that are completed or processing
if (!in_array($order->get_status(), ['completed', 'processing'])) {
$skipped_orders[] = $order_id;
$skipped_reasons[$order_id] = 'Status not completed/processing';
continue;
}
// Gather customer info and order details
$first_name = $order->get_billing_first_name();
$last_name = $order->get_billing_last_name();
$email = $order->get_billing_email();
$purchase_id = $order->get_id();
$txn_id = $order->get_transaction_id();
$company_name = $order->get_billing_company();
$date_created = $order->get_date_created()->date('Y-m-d');
$user_id = $order->get_user_id();
// Billing length and interval for subscription
$slm_billing_length = SLM_API_Utility::get_slm_option('slm_billing_length');
$slm_billing_interval = SLM_API_Utility::get_slm_option('slm_billing_interval');
$subscr_id = $user_id;
// Check license type and set expiration date accordingly
if ($slm_lic_type === 'lifetime') {
$date_expiry = date('Y-m-d', strtotime("+120 years", strtotime($date_created)));
} else {
// Calculate expiration based on billing interval and length
switch ($slm_billing_interval) {
case 'years':
$date_expiry = date('Y-m-d', strtotime("+$slm_billing_length years", strtotime($date_created)));
break;
case 'months':
$date_expiry = date('Y-m-d', strtotime("+$slm_billing_length months", strtotime($date_created)));
break;
case 'days':
$date_expiry = date('Y-m-d', strtotime("+$slm_billing_length days", strtotime($date_created)));
break;
default:
$date_expiry = $date_created;
}
}
//SLM_Helper_Class::write_log("Interval: {$slm_billing_interval} - Length: {$slm_billing_length}");
$order_items = $order->get_items();
if (count($order_items) === 0) {
// Handle empty orders by adding a default product item
$product = wc_get_product($default_product_id);
if (!$product) {
//SLM_Helper_Class::write_log("Error: Product with ID {$default_product_id} does not exist.");
continue;
}
$item = new WC_Order_Item_Product();
$item->set_product_id($default_product_id);
$item->set_name($product->get_name());
$item->set_quantity(1);
$item->set_total($product->get_price());
if ($item->meta_exists('_slm_lic_key')) {
$skipped_orders[] = $order_id;
$skipped_reasons[$order_id] = 'Already has a license';
continue;
}
$license_data = [
'slm_action' => 'slm_create_new',
'lic_status' => 'pending',
'lic_type' => $slm_lic_type,
'first_name' => $first_name,
'last_name' => $last_name,
'email' => $email,
'purchase_id_' => $purchase_id,
'txn_id' => $txn_id,
'company_name' => $company_name,
'max_allowed_domains' => SLM_DEFAULT_MAX_DOMAINS,
'max_allowed_devices' => SLM_DEFAULT_MAX_DEVICES,
'date_created' => $date_created,
'date_expiry' => $date_expiry,
'product_ref' => $product->get_name(),
'current_ver' => SLM_API_Utility::get_slm_option('license_current_version'),
'until' => SLM_API_Utility::get_slm_option('license_until_version'),
'subscr_id' => $subscr_id,
'item_reference' => $order_id,
'slm_billing_length' => $slm_billing_length,
'slm_billing_interval' => $slm_billing_interval,
'secret_key' => KEY_API
];
$license_key = '';
$response = wp_remote_post(SLM_API_URL, [
'method' => 'POST',
'body' => $license_data,
'timeout' => 45,
'sslverify' => false,
]);
if (!is_wp_error($response)) {
$body = wp_remote_retrieve_body($response);
$api_response = json_decode($body, true);
if ($api_response && isset($api_response['result']) && $api_response['result'] === 'success') {
$license_key = sanitize_text_field($api_response['key']);
$success_count++;
$order->add_order_note(
// Translators: %s is the generated license key
sprintf(__('License Key generated: %s', 'slm-plus'), $license_key)
);
$generated_licenses[] = [
'license_key' => $license_key,
'order_id' => $order_id,
];
} else {
$failure_count++;
}
} else {
$failure_count++;
}
$order->add_item($item);
$order->save();
} else {
foreach ($order_items as $item) {
$product_id = $item->get_product_id();
$product_name = $item->get_name();
// Fetch the license key directly using the order ID
$existing_license_key = SLM_Utility::slm_get_license_by_order_id($order_id);
if ($existing_license_key) {
// Add the current order to associated orders for the existing license
SLM_Utility::slm_add_associated_order($existing_license_key, $order_id);
$skipped_orders[] = $order_id;
$skipped_reasons[$order_id] = 'Already has a license';
continue;
}
if ($item->meta_exists('_slm_lic_key')) {
$skipped_orders[] = $order_id;
$skipped_reasons[$order_id] = 'Already has a license';
continue;
}
$license_data['product_ref'] = $product_name;
$license_key = '';
$response = wp_remote_post(SLM_API_URL, [
'method' => 'POST',
'body' => $license_data,
'timeout' => 45,
'sslverify' => false,
]);
if (!is_wp_error($response)) {
$body = wp_remote_retrieve_body($response);
$api_response = json_decode($body, true);
if ($api_response && isset($api_response['result']) && $api_response['result'] === 'success') {
$license_key = sanitize_text_field($api_response['key']);
$success_count++;
$generated_licenses[] = ['license_key' => $license_key, 'order_id' => $order_id];
$item->add_meta_data('_slm_lic_key', $license_key, true);
$item->add_meta_data('License Key', $license_key, true);
$item->add_meta_data('License Type', $slm_lic_type, true);
$order->add_order_note(
// Translators: %s is the generated license key
sprintf(__('License Key generated: %s', 'slm-plus'), $license_key)
);
} else {
$failure_count++;
}
} else {
$failure_count++;
}
$item->save();
}
$order->save();
}
}
if (!empty($skipped_orders)) {
$response_data['html'] .= '<li><strong>' . sprintf(
// Translators: %1$d is the number of orders skipped
__('%1$d orders were skipped:', 'slm-plus'),
count($skipped_orders)
) . '</strong><ul>';
foreach ($skipped_orders as $order_id) {
if ($order_id !== 0) {
$order_link = admin_url('post.php?post=' . $order_id . '&action=edit');
// Translators: %1$d is the order ID, %2$s is the reason why the order was skipped, %3$s is the order view link
$response_data['html'] .= '<li>' . sprintf(__('Order ID %1$d was skipped due to: %2$s. <a href="%3$s" target="_blank">View Order</a>', 'slm-plus'), $order_id, esc_html($skipped_reasons[$order_id]), esc_url($order_link)) . '</li>';
} else {
$response_data['html'] .= '<li>' . esc_html($skipped_reasons[0]) . '</li>';
}
}
$response_data['html'] .= '</ul></li>';
}
if ($success_count > 0) {
$response_data['html'] .= '<li><strong>' . sprintf(
// Translators: %1$d is the number of successfully generated licenses
__('%1$d licenses generated successfully:', 'slm-plus'),
$success_count
) . '</strong><ul>';
foreach ($generated_licenses as $license_data) {
$order_link = admin_url('post.php?post=' . $license_data['order_id'] . '&action=edit');
// Translators: %1$s is the license key, %2$d is the order ID, %3$s is the order view link
$response_data['html'] .= '<li>' . sprintf(__('License Key: %1$s for Order ID %2$d - <a href="%3$s" target="_blank">View Order</a>', 'slm-plus'), esc_html($license_data['license_key']), $license_data['order_id'], esc_url($order_link)) . '</li>';
}
$response_data['html'] .= '</ul></li>';
}
if ($failure_count > 0) {
$response_data['html'] .= '<li><strong>' . sprintf(
// Translators: %1$d is the number of licenses that failed to generate
__('%1$d licenses failed to generate.', 'slm-plus'),
$failure_count
) . '</strong></li>';
}
wp_send_json_success($response_data);
}