logger = Qixit_Logger::get_instance();
// Inject paid-comment CSS/JS through WordPress enqueue APIs.
add_action('wp_enqueue_scripts', array($this, 'inject_comment_scripts'));
// Server-side guard: block free/native comment submissions on paid-comment posts.
add_filter('preprocess_comment', array($this, 'enforce_paid_comment_submission'), 1);
// AJAX handlers for comment submission
add_action('wp_ajax_qixit_submit_paid_comment', array($this, 'ajax_submit_paid_comment'));
add_action('wp_ajax_nopriv_qixit_submit_paid_comment', array($this, 'ajax_submit_paid_comment'));
// Auto-enable WordPress comments when paid comments are enabled
add_action('save_post', array($this, 'auto_enable_comments_on_save'), 20, 3);
}
/**
* Auto-enable WordPress comments and sync comments product when paid comments are enabled
*/
public function auto_enable_comments_on_save($post_id, $post, $update) {
if (!empty($GLOBALS['qixit_skip_save_hooks'])) {
return;
}
// Skip autosaves and revisions
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (wp_is_post_revision($post_id)) return;
if (!$post instanceof WP_Post) {
$post = get_post($post_id);
}
if (!$post instanceof WP_Post) {
return;
}
// Check if paid comments are being enabled OR unpaid comments are explicitly allowed.
// save_post originates from WP editor submit; nonce is validated by metabox save handlers.
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- state change is protected by editor save flow nonce checks.
$enable_paid_comments = isset($_POST['qixit_enable_paid_comments']) && sanitize_text_field(wp_unslash($_POST['qixit_enable_paid_comments'])) === '1';
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- state change is protected by editor save flow nonce checks.
$allow_unpaid_comments = isset($_POST['qixit_allow_unpaid_comments']) && sanitize_text_field(wp_unslash($_POST['qixit_allow_unpaid_comments'])) === '1';
if ($allow_unpaid_comments) {
update_post_meta($post_id, '_qixit_allow_unpaid_comments', '1');
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- state change is protected by editor save flow nonce checks.
} elseif (isset($_POST['qixit_allow_unpaid_comments'])) {
delete_post_meta($post_id, '_qixit_allow_unpaid_comments');
}
if ($enable_paid_comments || $allow_unpaid_comments) {
// Ensure WordPress comments are open for this post (no nested save_post).
if ($post->comment_status !== 'open') {
$GLOBALS['qixit_skip_save_hooks'] = true;
wp_update_post(array(
'ID' => $post_id,
'comment_status' => 'open'
));
$GLOBALS['qixit_skip_save_hooks'] = false;
$this->logger->log("Auto-enabled WordPress comments for post {$post_id} (paid or unpaid comments enabled)");
}
// Auto-sync the comments product to the API
$product_manager = Qixit_Product::get_instance();
$product = $product_manager->get_product_by_post_id($post_id);
$existing_comments_guid = $product_manager->get_comments_purchase_guid_for_post($post_id, $product);
if ($enable_paid_comments && $product && empty($existing_comments_guid)) {
$this->logger->log("Auto-syncing comments product for post {$post_id}");
try {
$product_manager->sync_product_to_api($post_id);
} catch (\Throwable $e) {
$this->logger->log('Auto-sync comments product failed: ' . $e->getMessage(), 'error');
}
}
}
}
/**
* Enable paid comments for a post (called from bulk actions)
*/
public static function enable_paid_comments_for_post($post_id, $price = 0.05, $sync_to_api = true) {
global $wpdb;
$product_manager = Qixit_Product::get_instance();
$product = $product_manager->get_product_by_post_id($post_id);
// Ensure minimum price
$price = max(0.01, floatval($price));
if ($product) {
$product_manager->update_local_product($product->product_id, array(
'enable_paid_comments' => 1,
'premium_comments_cost' => $price
));
} else {
$product_manager->create_product_for_post($post_id, array(
'qixit_post_type' => QIXIT_REG,
'enable_paid_comments' => 1,
'premium_comments_cost' => $price
));
}
// Auto-enable WordPress comments
wp_update_post(array(
'ID' => $post_id,
'comment_status' => 'open'
));
// Sync to API to create comments product when requested.
if ($sync_to_api) {
$product_manager->sync_product_to_api($post_id);
}
return true;
}
/**
* Disable paid comments for a post
*/
public static function disable_paid_comments_for_post($post_id) {
$product_manager = Qixit_Product::get_instance();
$product = $product_manager->get_product_by_post_id($post_id);
if ($product) {
$product_manager->update_local_product($product->product_id, array(
'enable_paid_comments' => 0,
'premium_comments_cost' => 0
));
}
return true;
}
/**
* Whether site-level fallback paid comments are enabled.
*
* @return bool
*/
public static function is_global_paid_comments_enabled() {
return boolval(get_option('qixit_enable_paid_comments_global', false));
}
/**
* Site-level default comment price for global fallback.
*
* @return float
*/
public static function get_global_paid_comments_default_price() {
$price = floatval(get_option('qixit_global_paid_comments_price', 0.05));
return max(0.01, round($price, 2));
}
/**
* Per-post opt-out for global paid comments fallback.
*
* @param int $post_id
* @return bool
*/
private static function is_global_paid_comments_opted_out($post_id) {
return get_post_meta(intval($post_id), '_qixit_paid_comments_opt_out', true) === '1';
}
/**
* Determine effective paid-comments state for a post.
* Priority:
* 1) Explicit per-post paid-comments setting
* 2) Site-level fallback setting
* Explicit unpaid/opt-out flags always disable paid comments.
*
* @param int $post_id
* @param object|null $product
* @return bool
*/
public static function is_paid_comments_effectively_enabled($post_id, $product = null) {
$post_id = intval($post_id);
if ($post_id <= 0) {
return false;
}
if (get_post_meta($post_id, '_qixit_allow_unpaid_comments', true) === '1') {
return false;
}
if (self::is_global_paid_comments_opted_out($post_id)) {
return false;
}
if ($product === null) {
$product = Qixit_Product::get_instance()->get_product_by_post_id($post_id);
}
if ($product && isset($product->enable_paid_comments) && intval($product->enable_paid_comments) === 1) {
return true;
}
return self::is_global_paid_comments_enabled();
}
/**
* Determine effective comment price for a post.
*
* @param int $post_id
* @param object|null $product
* @return float
*/
public static function get_effective_comment_price($post_id, $product = null) {
if ($product === null) {
$product = Qixit_Product::get_instance()->get_product_by_post_id($post_id);
}
if ($product && isset($product->premium_comments_cost) && floatval($product->premium_comments_cost) > 0) {
return max(0.01, floatval($product->premium_comments_cost));
}
return self::get_global_paid_comments_default_price();
}
/**
* Inject JavaScript to make comment submit button use microframe
* Uses the same pattern as product purchase links - tooltip on hover, microframe on click
*/
public function inject_comment_scripts() {
// Only on singular posts/pages with comments
if (!is_singular() || !comments_open()) {
return;
}
// Ensure jQuery is available for comment-form interception.
wp_enqueue_script('jquery');
// In some themes/footer contexts get_the_ID() can be empty in wp_footer.
// Resolve from queried object first, then fall back to loop/global post IDs.
$post_id = intval(get_queried_object_id());
if ($post_id <= 0) {
$post_id = intval(get_the_ID());
}
if ($post_id <= 0 && isset($GLOBALS['post']) && isset($GLOBALS['post']->ID)) {
$post_id = intval($GLOBALS['post']->ID);
}
if ($post_id <= 0) {
$this->logger->log('Qixit Comments: Unable to resolve post ID in footer; skipping paid-comment script injection.', 'warning');
return;
}
$product_manager = Qixit_Product::get_instance();
$product = $product_manager->get_product_by_post_id($post_id);
// Check if paid comments are enabled (per-post or global fallback).
if (!self::is_paid_comments_effectively_enabled($post_id, $product)) {
return;
}
// Ensure local product context exists when global fallback is active.
if (!$product) {
self::enable_paid_comments_for_post($post_id, self::get_global_paid_comments_default_price(), false);
$product = $product_manager->get_product_by_post_id($post_id);
}
$comments_cost = self::get_effective_comment_price($post_id, $product);
$comments_guid = $product_manager->get_comments_purchase_guid_for_post($post_id, $product);
// If paid comments are enabled but GUID is missing, attempt a single auto-sync
// to provision/resolve the canonical comments product for this post context.
if (empty($comments_guid)) {
$sync_lock_key = 'qixit_comments_guid_sync_lock_' . $post_id;
if (!get_transient($sync_lock_key)) {
set_transient($sync_lock_key, 1, 30);
$product_manager->sync_product_to_api($post_id);
$product = $product_manager->get_product_by_post_id($post_id);
$comments_guid = $product_manager->get_comments_purchase_guid_for_post($post_id, $product);
}
} elseif ($comments_cost > 0) {
// Keep shared comments product price/delivery URL aligned with this post before checkout.
$api_price = function_exists('qixit_get_product_price') ? qixit_get_product_price($comments_guid) : 0;
if ($api_price <= 0 || abs($api_price - $comments_cost) > 0.0001) {
$sync_lock_key = 'qixit_comments_price_sync_' . $post_id;
if (!get_transient($sync_lock_key)) {
set_transient($sync_lock_key, 1, 60);
$product_manager->sync_comments_product_to_api($post_id, $product);
delete_transient('qixit_product_price_' . $comments_guid);
$api_price = function_exists('qixit_get_product_price') ? qixit_get_product_price($comments_guid) : 0;
}
}
if ($api_price > 0) {
$comments_cost = $api_price;
}
}
// Get products portal URL for purchase
$products_web_url = qixit_get_products_web_url();
$purchase_url = '';
if (!empty($comments_guid)) {
$purchase_url = rtrim($products_web_url, '/') . '/purchasefast?PID=' . urlencode($comments_guid);
}
// Format price for tooltip (2 decimal places)
$price_display = '$' . number_format($comments_cost, 2);
if (class_exists('Qixit_Frontend')) {
Qixit_Frontend::require_assets();
}
wp_enqueue_script('qixit-frontend');
wp_add_inline_style('qixit-frontend', '
.qixit-comment-tooltip-wrapper {
position: relative;
display: inline-block;
}
.qixit-comment-tooltip-wrapper .qixit-comment-tooltip {
visibility: hidden;
opacity: 0;
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, var(--qixit-brand-start, #52ac5b) 0%, var(--qixit-brand-end, #306817) 100%);
color: white;
padding: 6px 12px;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
white-space: nowrap;
z-index: 10000;
box-shadow: 0 2px 8px rgba(48, 104, 23, 0.35);
transition: opacity 0.2s, visibility 0.2s;
}
.qixit-comment-tooltip-wrapper .qixit-comment-tooltip::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: var(--qixit-brand-end, #306817);
}
.qixit-comment-tooltip-wrapper:hover .qixit-comment-tooltip {
visibility: visible;
opacity: 1;
}
');
$comment_config = array(
'postId' => intval($post_id),
'price' => floatval($comments_cost),
'priceDisplay' => $price_display,
'purchaseUrl' => $purchase_url,
'commentsGuid' => $comments_guid,
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('qixit_comment_nonce'),
);
wp_add_inline_script('qixit-frontend', 'window.qixitCommentConfig = ' . wp_json_encode($comment_config) . ';', 'before');
wp_add_inline_script('qixit-frontend', <<<'JS'
(function($) {
'use strict';
if (typeof $ === 'undefined') {
console.warn('Qixit Comments: jQuery not available; skipping paid comment JS initialization.');
return;
}
var qixitCommentConfig = window.qixitCommentConfig || {};
var pendingComment = null;
var messageHandlerBound = false;
$(document).ready(function() {
initCommentPayment();
});
function initCommentPayment() {
// Bind at form level first so theme-specific submit buttons cannot bypass interception.
var $form = $('#commentform, form[action*="wp-comments-post.php"]').first();
if (!$form.length) {
console.log('Qixit Comments: No comment form found');
return;
}
// Find submit control if present (input/button variants).
var $submitBtn = $form.find('#submit, input[type="submit"], button[type="submit"]').first();
// Check if comments product is configured
if (!qixitCommentConfig.purchaseUrl) {
console.log('Qixit Comments: Paid comments enabled but product not configured. GUID: ' + qixitCommentConfig.commentsGuid);
return;
}
console.log('Qixit Comments: Initializing with price ' + qixitCommentConfig.priceDisplay);
// Wrap button and add tooltip (positioned above) when submit control exists.
if ($submitBtn.length && !$submitBtn.parent().hasClass('qixit-comment-tooltip-wrapper')) {
$submitBtn.wrap('');
$submitBtn.parent().append('');
}
function startPaidCommentFlow(e) {
// If we have a pending paid comment, allow submission
if (pendingComment && pendingComment.paid) {
return true;
}
if (e && typeof e.preventDefault === 'function') {
e.preventDefault();
}
if (e && typeof e.stopImmediatePropagation === 'function') {
e.stopImmediatePropagation();
}
// Get comment data
var $comment = $form.find('textarea[name="comment"], #comment');
var $author = $form.find('input[name="author"]');
var $email = $form.find('input[name="email"]');
var $url = $form.find('input[name="url"]');
var $parent = $form.find('input[name="comment_parent"]');
if (!$comment.length || !$comment.val().trim()) {
alert('Please enter a comment.');
return false;
}
// Store comment data
pendingComment = {
comment: $comment.val(),
author: $author.length ? $author.val() : '',
email: $email.length ? $email.val() : '',
url: $url.length ? $url.val() : '',
parent: $parent.length ? $parent.val() : '0',
postId: qixitCommentConfig.postId,
paid: false
};
// Store in sessionStorage
sessionStorage.setItem('qixit_pending_comment_' + qixitCommentConfig.postId, JSON.stringify(pendingComment));
// Bind message handler if not already bound
if (!messageHandlerBound) {
window.addEventListener('message', handlePaymentMessage);
messageHandlerBound = true;
}
// Get anchor position for microframe placement (prefer submit button; fallback to form box).
var anchorEl = $submitBtn.length ? $submitBtn[0] : $form[0];
var buttonRect = anchorEl.getBoundingClientRect();
var clickX = buttonRect.left + (buttonRect.width / 2) + window.pageXOffset;
var clickY = buttonRect.top + window.pageYOffset; // Top anchor so frame opens above
// Open microframe using QixitFrontend
if (typeof window.QixitFrontend !== 'undefined') {
// Create a temporary trigger element
var $fakeTrigger = $('')
.attr('data-purchase-url', qixitCommentConfig.purchaseUrl)
.attr('data-product-guid', qixitCommentConfig.commentsGuid)
.attr('data-post-id', qixitCommentConfig.postId)
.attr('data-comment-checkout', 'true')
.css({ position: 'absolute', left: '-9999px' });
$('body').append($fakeTrigger);
window.QixitFrontend.openMicroFrame($fakeTrigger[0], true, clickX, clickY);
$fakeTrigger.remove();
} else {
// Fallback: open in popup
window.open(qixitCommentConfig.purchaseUrl, 'qixit_payment', 'width=400,height=300');
}
return false;
}
$form.off('submit.qixitPaidComment').on('submit.qixitPaidComment', startPaidCommentFlow);
$submitBtn.off('click.qixitPaidComment').on('click.qixitPaidComment', startPaidCommentFlow);
}
function handlePaymentMessage(event) {
if (!event.data) {
return;
}
var isPurchaseComplete = event.data.type === 'purchaseComplete';
var isPurchaseRedirect = event.data.type === 'redirect';
if (!isPurchaseComplete && !isPurchaseRedirect) {
return;
}
console.log('Qixit Comments: Received purchase callback', event.data);
// Get stored comment
var stored = sessionStorage.getItem('qixit_pending_comment_' + qixitCommentConfig.postId);
if (!stored) {
console.log('Qixit Comments: No pending comment found');
return;
}
pendingComment = JSON.parse(stored);
// Get username from message
var qixitUsername = event.data.username || event.data.qixitUser || '';
if (!qixitUsername) {
console.log('Qixit Comments: No username in payment response');
// Try to get from userId as fallback
qixitUsername = event.data.userId || 'Anonymous';
}
pendingComment.qixitUsername = qixitUsername;
pendingComment.paid = true;
pendingComment.txnId = event.data.transactionId || event.data.txnId || '';
sessionStorage.setItem('qixit_pending_comment_' + qixitCommentConfig.postId, JSON.stringify(pendingComment));
console.log('Qixit Comments: Submitting comment for user ' + qixitUsername);
if (window.QixitFrontend && typeof window.QixitFrontend.closeMicroFrame === 'function') {
window.QixitFrontend.closeMicroFrame();
}
// Submit comment via AJAX
submitPaidComment();
}
function submitPaidComment() {
if (!pendingComment || !pendingComment.paid) {
console.log('Qixit Comments: No paid comment to submit');
return;
}
console.log('Qixit Comments: Posting comment via AJAX');
$.ajax({
url: qixitCommentConfig.ajaxUrl,
type: 'POST',
data: {
action: 'qixit_submit_paid_comment',
nonce: qixitCommentConfig.nonce,
post_id: pendingComment.postId,
comment: pendingComment.comment,
author: pendingComment.author,
email: pendingComment.email,
url: pendingComment.url,
parent: pendingComment.parent,
qixit_username: pendingComment.qixitUsername,
txn_id: pendingComment.txnId || ''
},
success: function(response) {
console.log('Qixit Comments: AJAX response', response);
// Clear stored data
sessionStorage.removeItem('qixit_pending_comment_' + qixitCommentConfig.postId);
pendingComment = null;
if (response.success) {
// Refresh page and scroll to new comment
var commentId = response.data.comment_id;
var currentUrl = window.location.href.split('#')[0].split('?')[0];
window.location.href = currentUrl + '#comment-' + commentId;
window.location.reload();
} else {
alert('Error posting comment: ' + (response.data ? response.data.message : 'Unknown error'));
}
},
error: function(xhr, status, error) {
console.log('Qixit Comments: AJAX error', status, error);
alert('Error posting comment. Please try again.');
}
});
}
})(window.jQuery);
JS
);
}
/**
* AJAX handler for submitting paid comments
*/
public function ajax_submit_paid_comment() {
$this->logger->log("ajax_submit_paid_comment called");
// Verify nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'qixit_comment_nonce')) {
$this->logger->log("Nonce verification failed");
wp_send_json_error(array('message' => __('Security check failed.', '2waymicropay')));
}
$post_id = isset($_POST['post_id']) ? intval(wp_unslash($_POST['post_id'])) : 0;
$comment_text = isset($_POST['comment']) ? sanitize_textarea_field(wp_unslash($_POST['comment'])) : '';
$author = isset($_POST['author']) ? sanitize_text_field(wp_unslash($_POST['author'])) : '';
$email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '';
$url = isset($_POST['url']) ? esc_url_raw(wp_unslash($_POST['url'])) : '';
$parent = isset($_POST['parent']) ? intval(wp_unslash($_POST['parent'])) : 0;
$qixit_username = isset($_POST['qixit_username']) ? sanitize_text_field(wp_unslash($_POST['qixit_username'])) : '';
$txn_id = isset($_POST['txn_id']) ? sanitize_text_field(wp_unslash($_POST['txn_id'])) : '';
$this->logger->log("Comment data: post_id={$post_id}, author={$author}, qixit_username={$qixit_username}, txn_id={$txn_id}");
// Validate required fields
if (empty($comment_text)) {
wp_send_json_error(array('message' => __('Comment text is required.', '2waymicropay')));
}
if (empty($qixit_username)) {
wp_send_json_error(array('message' => __('Payment verification failed.', '2waymicropay')));
}
// Check if paid comments are enabled for this post
$product = Qixit_Product::get_instance()->get_product_by_post_id($post_id);
if (!self::is_paid_comments_effectively_enabled($post_id, $product)) {
$this->logger->log("Paid comments not enabled for post {$post_id}");
wp_send_json_error(array('message' => __('Paid comments are not enabled for this post.', '2waymicropay')));
}
$product_manager = Qixit_Product::get_instance();
$comments_guid = $product_manager->get_comments_purchase_guid_for_post($post_id, $product);
if ($comments_guid === '') {
wp_send_json_error(array('message' => __('Comment product is not configured.', '2waymicropay')));
}
$payment_verified = false;
if ($txn_id !== '') {
$verify = qixit_verify_purchase_with_portal($txn_id, $comments_guid, $qixit_username, null);
$payment_verified = !empty($verify['success']);
if (!$payment_verified) {
$this->logger->log(
"Paid comment txn verification failed for {$qixit_username} on {$comments_guid}: " .
(isset($verify['message']) ? $verify['message'] : 'unknown'),
'warning'
);
}
}
if (!$payment_verified) {
$ownership = Qixit_API::get_instance()->check_ownership($comments_guid, $qixit_username);
$payment_verified = (
!empty($ownership['success'])
&& !empty($ownership['data']['owned'])
);
}
if (!$payment_verified) {
$this->logger->log("Paid comment rejected — payment not verified for {$qixit_username} on {$comments_guid}", 'warning');
wp_send_json_error(array('message' => __('Payment verification failed.', '2waymicropay')));
}
// Get WordPress user if logged in
$user_id = 0;
if (is_user_logged_in()) {
$current_user = wp_get_current_user();
$user_id = $current_user->ID;
if (empty($author)) {
$author = $current_user->display_name;
}
if (empty($email)) {
$email = $current_user->user_email;
}
}
// If still no author, use Qixit username
if (empty($author)) {
$author = $qixit_username;
}
// Append Qixit username in parentheses after author name (for accountability)
$display_author = $author;
if ($author !== $qixit_username) {
$display_author = $author . ' (' . $qixit_username . ')';
}
// Insert the comment with auto-approve
$comment_data = array(
'comment_post_ID' => $post_id,
'comment_content' => $comment_text,
'comment_author' => $display_author,
'comment_author_email' => $email,
'comment_author_url' => $url,
'comment_parent' => $parent,
'user_id' => $user_id,
'comment_approved' => 1 // Auto-approve paid comments
);
$this->logger->log("Inserting comment: " . json_encode($comment_data));
$comment_id = wp_insert_comment($comment_data);
if ($comment_id) {
// Save metadata for internal tracking (not displayed publicly)
update_comment_meta($comment_id, 'qixit_paid_comment', 1);
update_comment_meta($comment_id, 'qixit_username', $qixit_username);
update_comment_meta($comment_id, 'qixit_comment_paid_date', current_time('mysql'));
if ($txn_id !== '') {
update_comment_meta($comment_id, 'qixit_comment_txn_id', $txn_id);
}
$this->logger->log("Paid comment #{$comment_id} posted by Qixit user: {$qixit_username}");
wp_send_json_success(array(
'message' => __('Comment posted successfully!', '2waymicropay'),
'comment_id' => $comment_id
));
} else {
$this->logger->log("Failed to insert comment");
wp_send_json_error(array('message' => __('Failed to post comment.', '2waymicropay')));
}
}
/**
* Block native/free comment submissions when paid comments are required.
*
* @param array $comment_data Incoming comment payload.
* @return array
*/
public function enforce_paid_comment_submission($comment_data) {
$post_id = isset($comment_data['comment_post_ID']) ? intval($comment_data['comment_post_ID']) : 0;
if ($post_id <= 0) {
return $comment_data;
}
$product = Qixit_Product::get_instance()->get_product_by_post_id($post_id);
if (!self::is_paid_comments_effectively_enabled($post_id, $product)) {
return $comment_data;
}
// Allow only the plugin's paid AJAX flow for paid-comment posts.
// Require a valid nonce so native/free submissions cannot spoof this path.
if (defined('DOING_AJAX') && DOING_AJAX) {
$nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
if (!empty($nonce) && wp_verify_nonce($nonce, 'qixit_comment_nonce')) {
$action = isset($_POST['action']) ? sanitize_key(wp_unslash($_POST['action'])) : '';
if ($action === 'qixit_submit_paid_comment') {
return $comment_data;
}
}
}
$this->logger->log("Blocked native/free comment submit on paid-comment post {$post_id}");
wp_die(
esc_html__('This post requires paid comments. Please use the paid comment button to purchase before posting.', '2waymicropay'),
esc_html__('Payment Required', '2waymicropay'),
array('response' => 403)
);
}
/**
* Check if paid comments are enabled for a post
*/
public function is_paid_comments_enabled($post_id) {
$product = Qixit_Product::get_instance()->get_product_by_post_id($post_id);
return self::is_paid_comments_effectively_enabled($post_id, $product);
}
/**
* Get comment price for a post
*/
public function get_comment_price($post_id) {
$product = Qixit_Product::get_instance()->get_product_by_post_id($post_id);
return self::get_effective_comment_price($post_id, $product);
}
/**
* Apply site-level fallback paid-comment defaults across existing posts/pages.
* This enables rapid anti-spam monetization while preserving per-post overrides.
*
* @return array{processed:int,synced:int}
*/
public static function apply_global_paid_comments_defaults_to_existing_content() {
if (!self::is_global_paid_comments_enabled()) {
return array('processed' => 0, 'synced' => 0);
}
$post_ids = get_posts(array(
'post_type' => array('post', 'page'),
'post_status' => array('publish', 'future', 'draft', 'pending', 'private'),
'fields' => 'ids',
'numberposts' => -1,
));
if (!is_array($post_ids) || empty($post_ids)) {
return array('processed' => 0, 'synced' => 0);
}
$default_price = self::get_global_paid_comments_default_price();
$processed = 0;
$seed_post_id = 0;
foreach ($post_ids as $post_id) {
$post_id = intval($post_id);
if ($post_id <= 0) {
continue;
}
if (get_post_meta($post_id, '_qixit_allow_unpaid_comments', true) === '1') {
continue;
}
if (self::is_global_paid_comments_opted_out($post_id)) {
continue;
}
self::enable_paid_comments_for_post($post_id, $default_price, false);
$processed++;
if ($seed_post_id === 0) {
$seed_post_id = $post_id;
}
}
$synced = 0;
if ($seed_post_id > 0) {
$result = Qixit_Product::get_instance()->sync_product_to_api($seed_post_id);
if (!empty($result['success'])) {
$synced = 1;
}
}
return array('processed' => $processed, 'synced' => $synced);
}
}
// Initialize
Qixit_Comments::get_instance();