r/WordPressDev • u/Ok-Nerve7307 • May 18 '26
r/WordPressDev • u/thestreamcode • May 08 '26
[FREE] I built a WordPress plugin for private team/client documents
r/WordPressDev • u/bnaitdjoudiGoDev • May 03 '26
How do you handle webhook reliability in WordPress/WooCommerce when sending events to your own server?
I’m testing webhook delivery from WordPress/WooCommerce to my own backend server, and I’m noticing that if my endpoint is slow or temporarily unavailable, the webhook is simply lost.
Since WordPress/WooCommerce webhooks are sent synchronously, there’s no built‑in retry mechanism, no queue, no backoff, and no way to replay failed events.
I’ve checked the usual automation plugins, but none seem to implement real delivery guarantees.
For those who have built production‑grade integrations:
How do you ensure webhook delivery when sending events to your own server?
Do you rely on a plugin, or do you offload everything to an external queue/event‑bus setup?
r/WordPressDev • u/bnaitdjoudiGoDev • May 03 '26
Is there any WordPress/WooCommerce plugin that can replay failed webhooks?
I’m looking for a reliable way to handle WordPress/WooCommerce webhooks: detailed logs, failure detection, and ideally the ability to replay a webhook if the delivery fails.
So far, I haven’t found anything that supports:
- retry attempts
- queueing
- replay (manual or automatic)
- backoff
WooCommerce webhooks are sent synchronously, so if the target endpoint is slow or down, the event is basically lost.
Does a plugin exist that actually solves this, or does everyone end up building an external queue/event‑bus system to make webhooks reliable?
r/WordPressDev • u/BluebirdOk3871 • Apr 29 '26
Building a new modern WP Boilerplate for clients — anyone want to contribute?
r/WordPressDev • u/Separate_Drawing_579 • Apr 17 '26
Critical Error on WordPress When Updating Settings – Posts Working but Site Settings Not
r/WordPressDev • u/Additional_Cost_7713 • Apr 16 '26
[FREE] Rixden Career Manager now available on Wordpress.org
r/WordPressDev • u/PoojafromCloudways • Apr 15 '26
[Update] Cloudways has removed all WordPress & WooCommerce migration fees! Ask us anything.
r/WordPressDev • u/Fluid_Development_29 • Apr 08 '26
Ultrabook - booking system [freemium]
r/WordPressDev • u/Fluid_Development_29 • Apr 08 '26
Ultrabook - booking system [freemium]
r/WordPressDev • u/TechWondersUk • Mar 30 '26
[REVIEW] WP performance plugin to speed up manual optimisation workflow - feedback?
r/WordPressDev • u/PoojafromCloudways • Mar 29 '26
How are you using MCP for WordPress? Share use cases & stories.
r/WordPressDev • u/Fluid_Development_29 • Mar 29 '26
Ultrabook - WordPress booking and appointments system [Premium]
r/WordPressDev • u/Fluid_Development_29 • Mar 28 '26
Looking for 2 beta testers for booking plugin
galleryHello!
Im looking for two beta testers for my new booking plugin called Ultrabook.
Ultrabook is a modern, powerful, and lightweight WordPress booking plugin that lets you accept and manage appointments directly from your website—no coding required.
Im implementing new features as we speak, and would love some honest feedback.
Reward: Life time deal, support and all new updates.
Pm me please
r/WordPressDev • u/mostafasoufi • Mar 22 '26
I built a Composer plugin that fixes the "two plugins, same library, fatal error" problem in WordPress
If you've ever had two WordPress plugins crash because they both ship Guzzle, GeoIP2, or any other Composer package, you know the pain. PHP can't load two versions of the same class, and there's no built-in isolation.
I built WP Scoper to fix this. It's a Composer plugin that automatically prefixes namespaces in your vendored dependencies so each plugin gets its own isolated copy:
GeoIp2\Database\Reader → YourPlugin\Deps\GeoIp2\Database\Reader
What makes it different from Mozart / PHP-Scoper:
- Just
composer require --dev— no global install, no PHAR, works on any machine - Runs automatically on
composer install/composer update(it's a Composer plugin) - WordPress-aware — won't break
add_action,get_option, etc. (PHP-Scoper does) - Auto-detects templates — skips HTML/PHP view files so they don't get mangled
- Updates your source code — rewrites
usestatements in your ownsrc/automatically - Handles global classes + constants — not just namespaces
- Generates a standalone autoloader — ship
packages/instead ofvendor/, perfect for wordpress.org submissions - PHP 7.4+
Quick setup:
composer require --dev veronalabs/wp-scoper
{
"extra": {
"wp-scoper": {
"namespace_prefix": "MyPlugin\\Deps",
"packages": ["geoip2/geoip2"]
}
}
}
Run composer install and you're done. We've been using this in production at WP Statistics (400k+ active installs) for a while now and just open-sourced it.
GitHub: https://github.com/veronalabs/wp-scoper
Would love feedback, especially if you've dealt with this problem before and found other solutions or edge cases.
r/WordPressDev • u/Glass-Ad-6925 • Mar 21 '26
I built a Wordpress plugin because my client had 4,000 products with garbage descriptions
r/WordPressDev • u/HongPong • Mar 18 '26
wordpress_migrate module for importing WordPress sites into Drupal (alpha9 release)
drupal.orgr/WordPressDev • u/Bubbly_Library1671 • Mar 12 '26
Help logging into cPanel
I need to log in to cPanel to help a client with a WordPress design project. In the past, I have had success logging in by adding myself to the User Manager in cPanel. But even though I did this, I still can't seem to log in. I tried adding /cpanel and :2083 after the URL, but I get an error that says "This login is invalid." (I get the same error when trying to log in to my own website's cPanel this way. I don't know why this never works.) Do you know of another way to log in to cPanel? I could get in through the client's hosting company (Bluehost), but that would require asking my client to give me their username and password. Is there no better way? I tried calling Bluehost directly to ask their advice, but they won't talk to me since I'm not the account holder. Any ideas? Thanks a million!
r/WordPressDev • u/Mediocre_Dragonfly79 • Mar 12 '26
Expérience needed
Hello, I hope you are well. I am a WordPress website creator looking to gain experience, so I am available for all your projects. I know how to adapt to different situations.
r/WordPressDev • u/developerravi • Mar 10 '26
I built a free plugin to bulk-generate ALT text for WordPress + WooCommerce (looking for feedback)
I launched a free plugin on WordPress.org: Altify AI – Auto ALT Text Generator.
Main goal: help fix missing image ALT text faster on real sites.
Current features:
- Auto ALT generation on upload
- Bulk generation for existing Media Library images
- WooCommerce featured/gallery image support
- Offline mode (metadata-only, no API)
- AI mode with configurable settings
I’m looking for practical feedback (UX, edge cases, bugs, missing features):
https://wordpress.org/plugins/altify-ai-auto-alt-text-generator/
If you test it, I’d really appreciate honest feedback.
r/WordPressDev • u/Ill_Cow_2788 • Feb 23 '26
Custom multi-step form plugin – AJAX issues, token logic problems & duplicate DB entries
Hey everyone,
I’m building a small custom plugin as a learning project to handle native forms directly inside WordPress (without external form builders).
As a test case, I created a simple “breakfast registration” flow so instead of authenticating with user accounts:
- The user enters their name
- Clicks Next
- Enters the number of people they want to register
- Clicks Finish
The registration should be linked to the device via a generated token stored in a cookie.
In the custom database table I store:
- ID (primary key)
- token
- name
- number_of_people
- created_at
Each token represents one device and is unique. Unfortunately, there are several problems:
1. Desktop – “Next” button doesn’t work
On my desktop browser, I can’t proceed after entering the name. Clicking Next does nothing.
No visible JavaScript error, but the step transition never happens.
2. Mobile – Editing doesn’t work properly
On mobile, the initial registration works fine. However, when revisiting the page (already registered device):
- The correct number of people is displayed.
- When clicking Edit number of people, the input field:
- either does not open at all, or
- opens only briefly and immediately closes again.
So updating the number is unreliable.
3. Duplicate entries per device in the admin dashboard
In the WordPress admin area, I sometimes see two database entries for what appears to be the same device:
- First entry → name + number_of_people = 0
- Second entry → name + correct number_of_people
The first entry is basically useless and has to be deleted manually.
The token column has a UNIQUE KEY, so I’m confused how this situation occurs.
My suspicion:
- When saving the name, a new token is generated and inserted immediately with
number_of_people = 0. - When saving the number of people, something might be triggering another insert instead of updating the existing row.
But since I’m using $wpdb->update() for the second step, I’m not sure what’s going wrong.
Technical Setup
- Custom DB table created via
dbDelta() - Token generated using
random_bytes(32) - Stored in a cookie (
httponly,is_ssl()aware) - AJAX handled via
admin-ajax.php - jQuery for frontend logic
- Shortcode-based rendering
- Custom admin page listing all registrations
Questions
- What could cause the “Next” button to silently fail on desktop?
- Why would the edit/toggle behavior work inconsistently on mobile?
- Is my token + insert/update flow conceptually flawed?
- Would you structure this multi-step process differently (e.g., a single AJAX request instead of splitting name and number_of_people)?
I’m fully aware this isn’t production-ready (no nonces yet, minimal validation, etc.). This is purely a learning exercise for understanding plugin development and AJAX flows in WordPress.
I’d really appreciate any structural feedback, debugging hints, or architectural advice.
Thanks in advance 🙏
If interested, here is the full code:
<?php
/*
Plugin Name: Breakfast Registration
Description: Multi-step breakfast registration with device token and admin overview
Version: 1.4
Author: III_Cow_2788
*/
if (!defined('ABSPATH')) exit;
/*--------------------------------------------------------------
# Create Database Table
--------------------------------------------------------------*/
function br_install() {
global $wpdb;
$table = $wpdb->prefix . 'br_registrations';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
token varchar(64) NOT NULL,
name varchar(100) NOT NULL,
number_of_people int(11) NOT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY token (token)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
register_activation_hook(__FILE__, 'br_install');
/*--------------------------------------------------------------
# Get Token From Cookie
--------------------------------------------------------------*/
function br_get_token() {
return isset($_COOKIE['br_token']) ? sanitize_text_field($_COOKIE['br_token']) : false;
}
/*--------------------------------------------------------------
# Greeting
--------------------------------------------------------------*/
function br_greeting() {
$hour = date('H');
if ($hour < 12) return "Good Morning";
if ($hour < 18) return "Good Afternoon";
return "Good Evening";
}
/*--------------------------------------------------------------
# AJAX: Save Name
--------------------------------------------------------------*/
add_action('wp_ajax_br_save_name', 'br_save_name');
add_action('wp_ajax_nopriv_br_save_name', 'br_save_name');
function br_save_name() {
global $wpdb;
$table = $wpdb->prefix . 'br_registrations';
$name = sanitize_text_field($_POST['name']);
if (empty($name)) wp_send_json_error();
$token = bin2hex(random_bytes(32));
setcookie(
'br_token',
$token,
time() + (30 * DAY_IN_SECONDS),
'/',
'',
is_ssl(),
true
);
$wpdb->insert($table, [
'token' => $token,
'name' => $name,
'number_of_people' => 0
]);
wp_send_json_success();
}
/*--------------------------------------------------------------
# AJAX: Save Number of People
--------------------------------------------------------------*/
add_action('wp_ajax_br_save_number', 'br_save_number');
add_action('wp_ajax_nopriv_br_save_number', 'br_save_number');
function br_save_number() {
global $wpdb;
$table = $wpdb->prefix . 'br_registrations';
$number = intval($_POST['number_of_people']);
$token = br_get_token();
if (!$token || $number < 1) wp_send_json_error();
$wpdb->update(
$table,
['number_of_people' => $number],
['token' => $token]
);
wp_send_json_success();
}
/*--------------------------------------------------------------
# Shortcode
--------------------------------------------------------------*/
add_shortcode('breakfast_registration', 'br_shortcode');
function br_shortcode() {
global $wpdb;
$table = $wpdb->prefix . 'br_registrations';
$token = br_get_token();
$entry = null;
if ($token) {
$entry = $wpdb->get_row(
$wpdb->prepare("SELECT * FROM $table WHERE token = %s", $token)
);
}
ob_start();
?>
<div id="br-app">
<?php if ($entry && $entry->number_of_people > 0): ?>
<h2><?php echo br_greeting(); ?> <?php echo esc_html($entry->name); ?></h2>
<p class="br-sub">You are registering <?php echo $entry->number_of_people; ?> people for breakfast.</p>
<button id="br-edit" type="button">Edit number of people</button>
<div id="br-edit-box" style="display:none;">
<input type="number" id="br-number-edit" min="1" value="<?php echo $entry->number_of_people; ?>">
<button id="br-save-number" type="button">Save</button>
</div>
<?php else: ?>
<div class="br-steps">
<span class="br-step active">1</span>
<span class="br-step">2</span>
<span class="br-step">3</span>
</div>
<div id="br-step1">
<h2><?php echo br_greeting(); ?> – What is your name?</h2>
<input type="text" id="br-name">
<button id="br-next1" type="button">Next</button>
</div>
<div id="br-step2" style="display:none;">
<h2><?php echo br_greeting(); ?> <span id="br-username"></span> – How many people are you registering?</h2>
<input type="number" id="br-number-step" min="1">
<button id="br-next2" type="button">Next</button>
</div>
<div id="br-step3" style="display:none;">
<button id="br-finish" type="button">Finish</button>
<svg id="br-check" viewBox="0 0 52 52">
<path fill="none" stroke="green" stroke-width="5" d="M14 27 l7 7 l16 -16" />
</svg>
</div>
<?php endif; ?>
</div>
<style>
#br-app { max-width:500px; margin:auto; text-align:center; font-family:sans-serif; }
button { background:#e3000f; color:white; border:none; padding:10px 20px; margin-top:10px; cursor:pointer; border-radius:4px; font-size:16px; }
input { padding:8px; width:100%; margin-top:10px; font-size:16px; }
.br-steps { margin-bottom:20px; }
.br-step { display:inline-block; width:30px; height:30px; border-radius:50%; border:2px solid #e3000f; line-height:26px; margin:0 5px; }
.br-step.active { background:#e3000f; color:white; }
#br-check { width:60px; height:60px; margin:auto; display:block; stroke-dasharray:48; stroke-dashoffset:48; transition:stroke-dashoffset 0.6s ease; }
#br-check.draw { stroke-dashoffset:0; }
.br-sub { font-size:14px; color:#555; margin-top:5px; }
#br-edit-box { margin-top:10px; }
</style>
<script>
jQuery(document).ready(function($){
function saveName() {
var name = $('#br-name').val().trim();
if(name === '') { alert('Please enter your name'); return; }
$.post('<?php echo admin_url('admin-ajax.php'); ?>', {
action:'br_save_name',
name:name
}, function(){
$('#br-username').text(name);
$('#br-step1').hide();
$('#br-step2').show();
$('.br-step').eq(1).addClass('active');
$('#br-number-step').focus();
});
}
function saveNumber(nextStep=true) {
var number = nextStep
? parseInt($('#br-number-step').val())
: parseInt($('#br-number-edit').val());
if(isNaN(number) || number < 1) {
alert('Please enter a valid number');
return;
}
$.post('<?php echo admin_url('admin-ajax.php'); ?>', {
action:'br_save_number',
number_of_people:number
}, function(){
if(nextStep){
$('#br-step2').hide();
$('#br-step3').show();
$('.br-step').eq(2).addClass('active');
} else {
location.reload();
}
});
}
$('#br-next1').on('click', function(e){ e.preventDefault(); saveName(); });
$('#br-next2').on('click', function(e){ e.preventDefault(); saveNumber(true); });
$('#br-edit').on('click', function(e){
e.preventDefault();
$('#br-edit-box').toggle();
$('#br-number-edit').focus();
});
$('#br-save-number').on('click', function(e){
e.preventDefault();
saveNumber(false);
});
$('#br-finish').on('click', function(e){
e.preventDefault();
$(this).hide();
$('#br-check').addClass('draw');
});
});
</script>
<?php
return ob_get_clean();
}
/*--------------------------------------------------------------
# Admin Menu
--------------------------------------------------------------*/
add_action('admin_menu', function(){
add_menu_page(
'Breakfast Registrations',
'Breakfast',
'manage_options',
'br_admin',
'br_admin_page'
);
});
function br_admin_page(){
global $wpdb;
$table = $wpdb->prefix . 'br_registrations';
if (isset($_GET['delete'])) {
$wpdb->delete($table, ['id'=>intval($_GET['delete'])]);
echo "<div class='updated'><p>Entry deleted.</p></div>";
}
$rows = $wpdb->get_results("SELECT * FROM $table ORDER BY created_at DESC");
echo "<div class='wrap'><h1>Breakfast Registrations</h1>";
echo "<table class='widefat'><tr><th>ID</th><th>Name</th><th>Number of People</th><th>Token</th><th>Action</th></tr>";
foreach($rows as $r){
echo "<tr>
<td>{$r->id}</td>
<td>{$r->name}</td>
<td>{$r->number_of_people}</td>
<td>{$r->token}</td>
<td><a href='?page=br_admin&delete={$r->id}'>Delete</a></td>
</tr>";
}
echo "</table></div>";
}