PK qhYîļJßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
| Dir : /home2/medicu/.trash/Incomplete Order/ |
| Server: Linux tista.bd.svlogins.com 5.14.0-611.49.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Apr 21 16:39:08 EDT 2026 x86_64 IP: 103.159.37.114 |
| Dir : /home2/medicu/.trash/Incomplete Order/sohag-online-solution.php |
<?php
/**
* Plugin Name: Incomplete Order
* Version: 3.2
* Author: Sohag Islam
*/
if (!defined('ABSPATH')) exit;
/* ----------------------------
* Helpers
* ---------------------------- */
function sos_tbl() { global $wpdb; return $wpdb->prefix . 'sos_incomplete_orders'; }
function sos_get_ip() {
// proxy/CDN support
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim(end($parts));
}
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
function sos_valid_bd_phone($p) {
// 11 digits, starts with 01 and 2nd digit 3-9
return (bool) preg_match('/^(01[3-9][0-9]{8})$/', $p);
}
/* ----------------------------
* Activation: DB + CRON
* ---------------------------- */
register_activation_hook(__FILE__, function(){
global $wpdb;
$table = sos_tbl();
$charset = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS {$table} (
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(200) NOT NULL,
phone VARCHAR(30) NOT NULL,
address TEXT NOT NULL,
ip_address VARCHAR(64) DEFAULT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY phone (phone),
KEY created_at (created_at)
) {$charset};";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
if (!wp_next_scheduled('sos_cleanup_incomplete_hourly')) {
wp_schedule_event(time(), 'hourly', 'sos_cleanup_incomplete_hourly');
}
});
register_deactivation_hook(__FILE__, function(){
wp_clear_scheduled_hook('sos_cleanup_incomplete_hourly');
});
/* ----------------------------
* Checkout fields: Email optional; Name/Phone/Address required
* ---------------------------- */
add_filter('woocommerce_checkout_fields', function($fields){
if (isset($fields['billing']['billing_email'])) {
$fields['billing']['billing_email']['required'] = false;
unset($fields['billing']['billing_email']['validate']); // remove strict email validate
$fields['billing']['billing_email']['label'] = __('(Optional) Email');
}
if (isset($fields['billing']['billing_first_name'])) {
$fields['billing']['billing_first_name']['required'] = true;
$fields['billing']['billing_first_name']['label'] = 'āĻāĻĒāύāĻžāϰ āύāĻžāĻŽ';
}
if (isset($fields['billing']['billing_address_1'])) {
$fields['billing']['billing_address_1']['required'] = true;
$fields['billing']['billing_address_1']['label'] = 'āĻāĻĒāύāĻžāϰ āϏāĻŽā§āĻĒā§āϰā§āĻŖ āĻ āĻŋāĻāĻžāύāĻž';
}
if (isset($fields['billing']['billing_phone'])) {
$fields['billing']['billing_phone']['required'] = true;
$fields['billing']['billing_phone']['label'] = 'āĻāĻĒāύāĻžāϰ āĻĢā§āύ āύāĻžāĻŽā§āĻŦāĻžāϰ';
}
return $fields;
}, 20);
/* ----------------------------
* Server-side phone validation
* ---------------------------- */
add_action('woocommerce_checkout_process', function(){
$phone = isset($_POST['billing_phone']) ? sanitize_text_field($_POST['billing_phone']) : '';
if (!sos_valid_bd_phone($phone)) {
wc_add_notice('đ āĻāĻĒāύāĻžāϰ ā§§ā§§ āϏāĻāĻā§āϝāĻžāϰ āĻŽā§āĻŦāĻžāĻāϞ āύāĻžāĻŽā§āĻŦāĻžāϰ āϞāĻŋāĻā§āύ (01 āĻĻāĻŋā§ā§ āĻļā§āϰā§, 2nd digit 3-9)āĨ¤', 'error');
}
});
/* ----------------------------
* IP block ONLY AFTER successful order (10 minutes)
* - Check during checkout to block if still active
* - Set on thankyou if order not failed/cancelled/refunded
* ---------------------------- */
function sos_ip_blocked_msg_minutes_left($ip) {
$until = get_transient('sos_block_until_' . md5($ip));
if (!$until) return 0;
$sec = max(0, $until - time());
return (int) ceil($sec / 60);
}
add_action('woocommerce_checkout_process', function(){
$ip = sos_get_ip();
$block = get_transient('sos_block_flag_' . md5($ip));
if ($block) {
$left = sos_ip_blocked_msg_minutes_left($ip);
$left = ($left > 0) ? $left : 10;
wc_add_notice("đĢ āĻĒāϰāĻŦāϰā§āϤ⧠āĻ
āϰā§āĻĄāĻžāϰā§āϰ āĻāύā§āϝ {$left} āĻŽāĻŋāύāĻŋāĻ āĻ
āĻĒā§āĻā§āώāĻž āĻāϰā§āύāĨ¤", 'error');
}
});
add_action('woocommerce_thankyou', function($order_id){
if (!$order_id) return;
$order = wc_get_order($order_id);
if (!$order) return;
// Only block if actually a successful order (not failed/cancelled/refunded)
if ($order->has_status(array('failed','cancelled','refunded'))) return;
$ip = sos_get_ip();
set_transient('sos_block_flag_' . md5($ip), 1, 10 * MINUTE_IN_SECONDS);
set_transient('sos_block_until_' . md5($ip), time() + 10 * MINUTE_IN_SECONDS, 10 * MINUTE_IN_SECONDS);
});
/* ----------------------------
* Enqueue front-end JS (real-time phone check + robust AJAX save)
* ---------------------------- */
add_action('wp_enqueue_scripts', function(){
if (function_exists('is_checkout') && is_checkout()) {
wp_enqueue_script('sos-checkout', plugin_dir_url(__FILE__) . 'assets/sos-checkout.js', array('jquery'), '3.2', true);
wp_localize_script('sos-checkout', 'sosAjax', array(
'url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('sos_nonce')
));
}
});
/* ----------------------------
* AJAX: Save Incomplete Order (NEVER MISS)
* - Validates required fields & phone
* - Deduplicates rapid repeats (30s)
* ---------------------------- */
add_action('wp_ajax_sos_save_incomplete', 'sos_save_incomplete');
add_action('wp_ajax_nopriv_sos_save_incomplete', 'sos_save_incomplete');
function sos_save_incomplete() {
check_ajax_referer('sos_nonce', 'nonce');
$name = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : '';
$phone = isset($_POST['phone']) ? sanitize_text_field($_POST['phone']) : '';
$address = isset($_POST['address']) ? sanitize_textarea_field($_POST['address']) : '';
$ip = sos_get_ip();
if ($name === '' || $address === '' || $phone === '') {
wp_send_json_error(array('msg' => 'āϏāĻŦ āĻāϰ āĻĒā§āϰāĻŖ āĻāϰā§āύ'));
}
if (!sos_valid_bd_phone($phone)) {
wp_send_json_error(array('msg' => 'ā§§ā§§ āϏāĻāĻā§āϝāĻžāϰ āϏāĻ āĻŋāĻ āĻŽā§āĻŦāĻžāĻāϞ āύāĻžāĻŽā§āĻŦāĻžāϰ āĻĻāĻŋāύ (01[3-9]...)'));
}
global $wpdb;
$table = sos_tbl();
// Prevent spammy rapid duplicates from same IP within 30s
$last = $wpdb->get_var($wpdb->prepare("SELECT created_at FROM {$table} WHERE ip_address=%s ORDER BY created_at DESC LIMIT 1", $ip));
if ($last && strtotime($last) > (time() - 30)) {
wp_send_json_success(array('msg' => 'Already saved recently')); // not an error, just skip duplicate
}
$ok = $wpdb->insert($table, array(
'name' => $name,
'phone' => $phone,
'address' => $address,
'ip_address' => $ip,
'created_at' => current_time('mysql')
), array('%s','%s','%s','%s','%s'));
if ($ok) {
wp_send_json_success(array('msg' => 'Saved'));
} else {
wp_send_json_error(array('msg' => 'Server error, try again'));
}
}
/* ----------------------------
* Admin: Incomplete Orders (List + Create Order + Delete)
* ---------------------------- */
add_action('admin_menu', function(){
add_menu_page(
'Incomplete Orders',
'Incomplete Orders',
'manage_woocommerce',
'sos-incomplete-orders',
'sos_admin_page',
'dashicons-list-view',
56
);
});
function sos_admin_page(){
if (!current_user_can('manage_woocommerce')) wp_die('Permission denied');
global $wpdb;
$table = sos_tbl();
// CREATE ORDER
if (isset($_GET['create']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'sos_create_'.intval($_GET['create']))) {
$id = intval($_GET['create']);
$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE id=%d", $id));
if ($row && function_exists('wc_create_order')) {
$order = wc_create_order();
// Set billing info
$order->set_billing_first_name($row->name);
$order->set_billing_phone($row->phone);
$order->set_billing_address_1($row->address);
// Optional: add a zero-fee line for visibility
$fee = new WC_Order_Item_Fee();
$fee->set_name('Created from Incomplete Order #'.$row->id);
$fee->set_amount(0);
$fee->set_total(0);
$order->add_item($fee);
$order->calculate_totals();
$order->save();
echo '<div class="notice notice-success is-dismissible"><p>Order created. ID: '.esc_html($order->get_id()).'</p></div>';
} else {
echo '<div class="notice notice-error is-dismissible"><p>Failed to create order.</p></div>';
}
}
// DELETE
if (isset($_GET['delete']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'sos_delete_'.intval($_GET['delete']))) {
$wpdb->delete($table, array('id'=>intval($_GET['delete'])), array('%d'));
echo '<div class="notice notice-success is-dismissible"><p>Deleted.</p></div>';
}
// LIST
$rows = $wpdb->get_results("SELECT * FROM {$table} ORDER BY created_at DESC");
echo '<div class="wrap"><h1>Incomplete Orders</h1>';
echo '<table class="widefat striped"><thead><tr>
<th>ID</th><th>Name</th><th>Phone</th><th>Address</th><th>IP</th><th>Created</th><th>Actions</th>
</tr></thead><tbody>';
if ($rows) {
foreach ($rows as $r) {
$create_url = wp_nonce_url(admin_url('admin.php?page=sos-incomplete-orders&create='.$r->id), 'sos_create_'.$r->id);
$delete_url = wp_nonce_url(admin_url('admin.php?page=sos-incomplete-orders&delete='.$r->id), 'sos_delete_'.$r->id);
echo '<tr>
<td>'.(int)$r->id.'</td>
<td>'.esc_html($r->name).'</td>
<td>'.esc_html($r->phone).'</td>
<td>'.esc_html($r->address).'</td>
<td>'.esc_html($r->ip_address).'</td>
<td>'.esc_html($r->created_at).'</td>
<td>
<a class="button" href="'.esc_url($create_url).'">Create Order</a>
<a class="button" style="background:#d63638;color:#fff" href="'.esc_url($delete_url).'">Delete</a>
</td>
</tr>';
}
} else {
echo '<tr><td colspan="7">No incomplete orders found.</td></tr>';
}
echo '</tbody></table></div>';
}
/* ----------------------------
* Cleanup old incomplete (24h)
* ---------------------------- */
add_action('sos_cleanup_incomplete_hourly', function(){
global $wpdb;
$table = sos_tbl();
$cut = gmdate('Y-m-d H:i:s', time() - 24*HOUR_IN_SECONDS);
$wpdb->query($wpdb->prepare("DELETE FROM {$table} WHERE created_at < %s", $cut));
});