From f8d4fe46a149809cfa1a77326028cb9b17f0ce92 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 6 Aug 2026 00:30:46 +0600 Subject: [PATCH 01/19] feat(order): add prepaid status --- includes/enums/order/status.php | 4 +++- includes/payments/functions.php | 1 + includes/repositories/order-repository.php | 3 ++- .../Version1/class-orders-controller.php | 2 ++ includes/setup/activation.php | 24 ++++++++++++++++++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/includes/enums/order/status.php b/includes/enums/order/status.php index b67531f9d8..c4ee9bd76c 100644 --- a/includes/enums/order/status.php +++ b/includes/enums/order/status.php @@ -6,6 +6,7 @@ class Status { const PENDING = 'pending'; + const PREPAID = 'prepaid'; const PAID = 'paid'; const FAILED = 'failed'; const CANCELLED = 'cancelled'; @@ -16,6 +17,7 @@ class Status { public static function all() { return [ self::PENDING, + self::PREPAID, self::PAID, self::FAILED, self::CANCELLED, @@ -24,4 +26,4 @@ public static function all() { self::UNPAID, ]; } -} \ No newline at end of file +} diff --git a/includes/payments/functions.php b/includes/payments/functions.php index 73106870b8..5cb3386b4c 100644 --- a/includes/payments/functions.php +++ b/includes/payments/functions.php @@ -45,6 +45,7 @@ function atbdp_get_payment_statuses() { $statuses = [ 'created' => __( "Created", 'directorist' ), 'pending' => __( "Pending", 'directorist' ), + 'prepaid' => __( "Prepaid", 'directorist' ), 'completed' => __( "Completed", 'directorist' ), 'failed' => __( "Failed", 'directorist' ), 'cancelled' => __( "Cancelled", 'directorist' ), diff --git a/includes/repositories/order-repository.php b/includes/repositories/order-repository.php index 67b9ebc600..9a4265dd95 100644 --- a/includes/repositories/order-repository.php +++ b/includes/repositories/order-repository.php @@ -11,6 +11,7 @@ use Directorist\Helpers\DateTime; use Directorist\DTO\Order\DTO; use Directorist\Enums\Order\Status as OrderStatus; +use Directorist\Enums\Payment\Status as PaymentStatus; use Directorist\Enums\Order\TaxType as OrderTaxType; use Directorist\Enums\Order\DiscountType as OrderDiscountType; use Directorist\DTO\Order\Read; @@ -169,7 +170,7 @@ public function update( \Directorist\Utils\DTO $dto ) { do_action( 'directorist_after_order_update', $this->to_dto( $updated_order ), $old_order ); - if ( $dto->is_initialized( 'status' ) ) { + if ( $dto->is_initialized( 'status' ) && in_array( $dto->get_status(), PaymentStatus::all(), true ) ) { ( new PaymentRepository )->update_status_by_order_id( $dto->get_id(), $dto->get_status() ); } diff --git a/includes/rest-api/Version1/class-orders-controller.php b/includes/rest-api/Version1/class-orders-controller.php index 4058166c37..afbea5c885 100644 --- a/includes/rest-api/Version1/class-orders-controller.php +++ b/includes/rest-api/Version1/class-orders-controller.php @@ -305,6 +305,7 @@ protected function legacy_status_to_current_status( string $status ): string { $map = array( 'created' => OrderStatus::PENDING, 'pending' => OrderStatus::PENDING, + 'prepaid' => OrderStatus::PREPAID, 'completed' => OrderStatus::PAID, 'failed' => OrderStatus::FAILED, 'cancelled' => OrderStatus::CANCELLED, @@ -317,6 +318,7 @@ protected function legacy_status_to_current_status( string $status ): string { protected function current_status_to_legacy_status( string $status ): string { $map = array( OrderStatus::PENDING => 'pending', + OrderStatus::PREPAID => 'prepaid', OrderStatus::PAID => 'completed', OrderStatus::FAILED => 'failed', OrderStatus::CANCELLED => 'cancelled', diff --git a/includes/setup/activation.php b/includes/setup/activation.php index f347f1034f..9f08fde7c0 100644 --- a/includes/setup/activation.php +++ b/includes/setup/activation.php @@ -21,6 +21,7 @@ public static function register_hooks() { public static function run() { // Run the activation tasks. self::create_tables(); + self::sync_order_status_enum(); } public static function create_tables() { @@ -77,4 +78,25 @@ public static function create_tables() { } ); } -} \ No newline at end of file + + private static function sync_order_status_enum() { + global $wpdb; + + $table_name = $wpdb->prefix . 'directorist_orders'; + + if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) !== $table_name ) { + return; + } + + $statuses = array_map( + function( $status ) { + return "'" . esc_sql( $status ) . "'"; + }, + OrderStatus::all() + ); + + $wpdb->query( + "ALTER TABLE {$table_name} MODIFY status ENUM(" . implode( ',', $statuses ) . ") DEFAULT '" . esc_sql( OrderStatus::PENDING ) . "'" + ); + } +} From c6146117c1ded325c9df6d3b34a9bd167a8a5a08 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 6 Aug 2026 22:35:45 +0600 Subject: [PATCH 02/19] fix: include directory slug in author profile links --- includes/classes/class-permalink.php | 20 ++++++++++++++++---- templates/single/section-author_info.php | 15 ++++++++------- templates/widgets/author-info.php | 17 +++++++++-------- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/includes/classes/class-permalink.php b/includes/classes/class-permalink.php index f3f9776a33..52c615c3aa 100644 --- a/includes/classes/class-permalink.php +++ b/includes/classes/class-permalink.php @@ -33,12 +33,26 @@ public static function get_listing_permalink( $post_id = 0, $permalink = '' ) { $permalink = get_the_permalink( $post_id ); } + $directory_slug = self::get_listing_directory_type_slug( $post_id ); + + $permalink = str_replace( '%' . ATBDP_DIRECTORY_TYPE . '%', $directory_slug, $permalink ); + + return $permalink; + } + + /** + * Get the directory type slug for a listing. + * + * @param int $post_id Listing ID. + * @return string + */ + public static function get_listing_directory_type_slug( $post_id = 0 ) { $directory_slug = ''; $directory_id = directorist_get_listing_directory( $post_id ); if ( $directory_id ) { $directory_term = get_term( $directory_id, ATBDP_DIRECTORY_TYPE ); - $directory_slug = $directory_term ? $directory_term->slug : ''; + $directory_slug = ( $directory_term && ! is_wp_error( $directory_term ) ) ? $directory_term->slug : ''; } if ( empty( $directory_slug ) ) { @@ -49,9 +63,7 @@ public static function get_listing_permalink( $post_id = 0, $permalink = '' ) { } } - $permalink = str_replace( '%' . ATBDP_DIRECTORY_TYPE . '%', $directory_slug, $permalink ); - - return $permalink; + return $directory_slug; } public static function get_listing_slug() { diff --git a/templates/single/section-author_info.php b/templates/single/section-author_info.php index dea02ad27a..ba6a63b198 100644 --- a/templates/single/section-author_info.php +++ b/templates/single/section-author_info.php @@ -9,12 +9,13 @@ if ( ! defined( 'ABSPATH' ) ) exit; -$id = $listing->id; -$author_id = $listing->author_id; -$u_pro_pic = get_user_meta( $author_id, 'pro_pic', true ); -$u_pro_pic = ! empty( $u_pro_pic ) ? wp_get_attachment_image_src( $u_pro_pic, 'thumbnail' ) : ''; -$author_img = ! empty( $u_pro_pic ) ? $u_pro_pic[0] : ''; -$avatar_img = get_avatar( $author_id, 32 ); +$id = $listing->id; +$author_id = $listing->author_id; +$directory_type = ATBDP_Permalink::get_listing_directory_type_slug( $id ); +$u_pro_pic = get_user_meta( $author_id, 'pro_pic', true ); +$u_pro_pic = ! empty( $u_pro_pic ) ? wp_get_attachment_image_src( $u_pro_pic, 'thumbnail' ) : ''; +$author_img = ! empty( $u_pro_pic ) ? $u_pro_pic[0] : ''; +$avatar_img = get_avatar( $author_id, 32 ); ?>
section_id( $id ); ?>> @@ -112,7 +113,7 @@ - + diff --git a/templates/widgets/author-info.php b/templates/widgets/author-info.php index 876f701ec7..3cc293665d 100644 --- a/templates/widgets/author-info.php +++ b/templates/widgets/author-info.php @@ -12,13 +12,14 @@
@@ -124,8 +125,8 @@ class="directorist-single-author-contact-info-text" - -
\ No newline at end of file +
From 2cb30a0a5fe9c9a0ce6a195f58772b34475d0770 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Mon, 17 Aug 2026 17:12:01 +0600 Subject: [PATCH 03/19] fix(order): preserve status column constraints --- includes/setup/activation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/setup/activation.php b/includes/setup/activation.php index 9f08fde7c0..ee76faa185 100644 --- a/includes/setup/activation.php +++ b/includes/setup/activation.php @@ -96,7 +96,7 @@ function( $status ) { ); $wpdb->query( - "ALTER TABLE {$table_name} MODIFY status ENUM(" . implode( ',', $statuses ) . ") DEFAULT '" . esc_sql( OrderStatus::PENDING ) . "'" + "ALTER TABLE {$table_name} MODIFY status ENUM(" . implode( ',', $statuses ) . ") NOT NULL DEFAULT '" . esc_sql( OrderStatus::PENDING ) . "'" ); } } From 9eec0b0dcc6cde03bfb225b36209aeb16aa36df7 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Mon, 17 Aug 2026 17:18:13 +0600 Subject: [PATCH 04/19] fix: include directory slug in author avatar link --- includes/model/SingleListing.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/model/SingleListing.php b/includes/model/SingleListing.php index 377c707c8e..e2f551a55f 100644 --- a/includes/model/SingleListing.php +++ b/includes/model/SingleListing.php @@ -1590,12 +1590,13 @@ public function user_avatar() { $user_pro_pic = get_user_meta( $this->author_id, 'pro_pic', true ); $u_pro_pic = ! empty( $u_pro_pic ) ? wp_get_attachment_image_src( $u_pro_pic, 'thumbnail' ) : ''; $author_data = get_userdata( $this->author_id ); + $directory_type = ATBDP_Permalink::get_listing_directory_type_slug( $this->id ); $author_first_name = ! empty( $author_data ) ? $author_data->first_name : ''; $author_last_name = ! empty( $author_data ) ? $author_data->last_name : ''; $args = [ - 'author_link' => ATBDP_Permalink::get_user_profile_page_link( $this->author_id ), + 'author_link' => ATBDP_Permalink::get_user_profile_page_link( $this->author_id, $directory_type ), 'u_pro_pic' => $u_pro_pic, 'avatar_img' => get_avatar( $this->author_id, apply_filters( 'atbdp_avatar_size', 32 ) ), 'author_full_name' => $author_first_name . ' ' . $author_last_name, From 1bc3a55b50925e3cbcacb148d01bb59f81c019a1 Mon Sep 17 00:00:00 2001 From: Armanul46 <47377178+Armanul46@users.noreply.github.com> Date: Tue, 18 Aug 2026 01:54:00 +0600 Subject: [PATCH 05/19] readme update --- config.php | 2 +- directorist-base.php | 2 +- readme.txt | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/config.php b/config.php index ebd94a5c14..85dec62a2b 100644 --- a/config.php +++ b/config.php @@ -1,7 +1,7 @@ Date: Wed, 19 Aug 2026 12:53:16 +0600 Subject: [PATCH 06/19] fix(featured-listing): sync expiration after order update --- .../class-featured-listing-checkout.php | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index e7ae39ef75..a09e81bd96 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -156,7 +156,11 @@ public function handle_after_order_update( OrderDTO $dto ) { // Publish the listing if it's pending $listing = get_post( $dto->get_listing_id() ); - + + if ( $listing ) { + $this->updated_listing_expiration( $listing, $dto ); + } + if ( $listing && 'publish' !== $listing->post_status ) { directorist_set_listing_status( $dto->get_listing_id(), 'publish' ); } @@ -165,6 +169,28 @@ public function handle_after_order_update( OrderDTO $dto ) { } } + private function updated_listing_expiration( $listing, OrderDTO $order ) { + if ( ! $order->is_initialized( 'expires_at' ) || get_post_meta( $listing->ID, '_never_expire', true ) ) { + return; + } + + $order_expiration = $order->get_expires_at(); + $listing_expiration = get_post_meta( $listing->ID, '_expiry_date', true ); + $listing_expiration_date = $listing_expiration + ? date_create_from_format( 'Y-m-d H:i:s', $listing_expiration, wp_timezone() ) + : false; + + if ( + $listing_expiration_date && + $listing_expiration === $listing_expiration_date->format( 'Y-m-d H:i:s' ) && + $listing_expiration_date->getTimestamp() >= $order_expiration->getTimestamp() + ) { + return; + } + + update_post_meta( $listing->ID, '_expiry_date', $order_expiration->format( 'Y-m-d H:i:s' ) ); + } + public function handle_payment_receipt_order_items( array $order_items, OrderDTO $order ) { if ( ! $this->is_featured_order( $order ) ) { return $order_items; From 9c4b14201aec7a0a9e417ef3f848fa636b9636f4 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Wed, 19 Aug 2026 13:07:36 +0600 Subject: [PATCH 07/19] fix(featured-listing): honor force-disabled state --- includes/classes/class-cron.php | 4 ++++ includes/classes/class-featured-listing-checkout.php | 9 +++++++-- includes/directorist-core-functions.php | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/includes/classes/class-cron.php b/includes/classes/class-cron.php index 8858b9a323..a80326cc6a 100644 --- a/includes/classes/class-cron.php +++ b/includes/classes/class-cron.php @@ -98,6 +98,10 @@ public function atbdp_custom_schedule_cron() { */ private function featured_listing_followup() { + if ( directorist_is_force_disabled_featured_listings() ) { + return; + } + if ( directorist_is_monetization_enabled() && directorist_is_featured_listing_enabled() ) { $featured_days = get_directorist_option( 'featured_listing_time', 30 ); // Define the query diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index a09e81bd96..26c4be8dd1 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -14,10 +14,15 @@ class FeaturedListingCheckout { const CHECKOUT_TYPE = 'featured_listing'; public function __construct() { - add_filter( 'directorist_checkout_types', [$this, 'add_checkout_type'] ); add_filter( 'directorist_order_data', [ $this, 'handle_order_data' ] ); - add_action( 'directorist_after_order_update', [$this, 'handle_after_order_update'] ); add_filter( 'directorist_payment_receipt_order_items', [$this, 'handle_payment_receipt_order_items'], 10, 2 ); + + if ( directorist_is_force_disabled_featured_listings() ) { + return; + } + + add_filter( 'directorist_checkout_types', [$this, 'add_checkout_type'] ); + add_action( 'directorist_after_order_update', [$this, 'handle_after_order_update'] ); add_filter( 'directorist_checkout_validation', [$this, 'validate_checkout'], 10, 2 ); add_action( 'directorist_checkout_table', [$this, 'handle_checkout_table'], 10, 4 ); add_filter( 'directorist_checkout_subtotal', [$this, 'handle_checkout_subtotal'], 10, 3 ); diff --git a/includes/directorist-core-functions.php b/includes/directorist-core-functions.php index 240cf293dd..e2d77dbb53 100644 --- a/includes/directorist-core-functions.php +++ b/includes/directorist-core-functions.php @@ -18,6 +18,13 @@ function directorist_is_guest_submission_enabled() { return (bool) get_directorist_option( 'guest_listings', false ); } +/** + * @return bool + */ +function directorist_is_force_disabled_featured_listings() { + return (bool) apply_filters( 'directorist_is_force_disabled_featured_listings', false ); +} + function directorist_is_featured_listing_enabled( array $context = [] ) { return (bool) apply_filters( 'directorist_is_featured_listing_enabled', get_directorist_option( 'enable_featured_listing' ), $context ); } From eb5329977ec6032d3fcc22a7f7c7a826154a6f83 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Wed, 19 Aug 2026 13:12:47 +0600 Subject: [PATCH 08/19] refactor(featured-listing): use new orders in expiration cron --- includes/classes/class-cron.php | 51 +++++++--------------- includes/repositories/order-repository.php | 10 +++++ 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/includes/classes/class-cron.php b/includes/classes/class-cron.php index a80326cc6a..e1f84d1597 100644 --- a/includes/classes/class-cron.php +++ b/includes/classes/class-cron.php @@ -103,7 +103,9 @@ private function featured_listing_followup() { } if ( directorist_is_monetization_enabled() && directorist_is_featured_listing_enabled() ) { - $featured_days = get_directorist_option( 'featured_listing_time', 30 ); + $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); + $orders = directorist_order_repository(); + $current_time = directorist_now()->getTimestamp(); // Define the query $args = [ 'post_type' => ATBDP_POST_TYPE, @@ -124,44 +126,23 @@ private function featured_listing_followup() { // Start the Loop if ( $listings->found_posts ) { foreach ( $listings->posts as $listing ) { - $order = $this->get_order_by_listing( $listing->ID ); - if ( $order ) { - $days = round( abs( strtotime( current_time( 'mysql' ) ) - strtotime( $order[0]->post_date ) ) / 86400 ); - if ( $days > $featured_days ) { - do_action( 'atbdp_listing_featured_to_general', $listing->ID ); - update_post_meta( $listing->ID, '_featured', '' ); - } - } - } - } - } - } + $order = $orders->get_latest_paid_featured_order_by_listing_id( $listing->ID ); - private function get_order_by_listing( $listing_id ) { - $args = [ - 'post_type' => ATBDP_ORDER_POST_TYPE, - 'posts_per_page' => 1, - 'post_status' => 'publish', - 'meta_query' => [ - 'relation' => 'AND', - [ - 'key' => '_listing_id', - 'value' => $listing_id, - ], - [ - 'key' => '_payment_status', - 'value' => 'completed', - ], - ], - ]; + if ( ! $order ) { + continue; + } - $listings = new WP_Query( $args ); + $expiration = ! empty( $order->expires_at ) + ? new \Directorist\Helpers\DateTime( $order->expires_at ) + : ( new \Directorist\Helpers\DateTime( $order->created_at ) )->add_days( $featured_days ); - // Start the Loop - if ( $listings->found_posts ) { - return $listings->posts; + if ( $expiration->getTimestamp() <= $current_time ) { + do_action( 'atbdp_listing_featured_to_general', $listing->ID ); + update_post_meta( $listing->ID, '_featured', '' ); + } + } + } } - return ''; } /** diff --git a/includes/repositories/order-repository.php b/includes/repositories/order-repository.php index 9a4265dd95..08f5181b0f 100644 --- a/includes/repositories/order-repository.php +++ b/includes/repositories/order-repository.php @@ -89,6 +89,16 @@ public function listing_has_paid_featured_order( int $listing_id ): bool { return $order ? true : false; } + public function get_latest_paid_featured_order_by_listing_id( int $listing_id ) { + return $this->get_query_builder() + ->select( 'd_order.created_at', 'd_order.expires_at' ) + ->where( 'd_order.listing_id', $listing_id ) + ->where( 'd_order.is_featured_listing', 1 ) + ->where( 'd_order.status', OrderStatus::PAID ) + ->order_by_desc( 'd_order.id' ) + ->first(); + } + protected function get_orders( Builder $query, Read $dto ) { $query = apply_filters( 'directorist_order_list_query', $query, $dto ); From 5af64bfc5a948fea5cbfe105f7c5a1b970d840c2 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Wed, 19 Aug 2026 15:51:47 +0600 Subject: [PATCH 09/19] fix(featured-listing): handle provider and order edge cases --- .../classes/class-featured-listing-checkout.php | 15 +++++++++------ includes/repositories/order-repository.php | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index 26c4be8dd1..c7c53f29f5 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -13,10 +13,14 @@ class FeaturedListingCheckout { const CHECKOUT_TYPE = 'featured_listing'; - public function __construct() { + public function __construct() { add_filter( 'directorist_order_data', [ $this, 'handle_order_data' ] ); add_filter( 'directorist_payment_receipt_order_items', [$this, 'handle_payment_receipt_order_items'], 10, 2 ); + add_action( 'plugins_loaded', [ $this, 'register_hooks' ], 20 ); + } + + public function register_hooks() { if ( directorist_is_force_disabled_featured_listings() ) { return; } @@ -185,10 +189,9 @@ private function updated_listing_expiration( $listing, OrderDTO $order ) { ? date_create_from_format( 'Y-m-d H:i:s', $listing_expiration, wp_timezone() ) : false; - if ( - $listing_expiration_date && - $listing_expiration === $listing_expiration_date->format( 'Y-m-d H:i:s' ) && - $listing_expiration_date->getTimestamp() >= $order_expiration->getTimestamp() + if ( $listing_expiration_date + && $listing_expiration === $listing_expiration_date->format( 'Y-m-d H:i:s' ) + && $listing_expiration_date->getTimestamp() >= $order_expiration->getTimestamp() ) { return; } @@ -237,7 +240,7 @@ public function handle_checkout_product_name( string $product_name, OrderDTO $dt } public function is_featured_order( OrderDTO $order_dto ): bool { - if ( ! $order_dto->is_initialized( 'ref_type' ) || ! $order_dto->is_initialized( 'ref' ) || ! $order_dto->is_initialized( 'listing_id' ) ) { + if ( ! $order_dto->is_initialized( 'ref_type' ) || ! $order_dto->is_initialized( 'listing_id' ) ) { return false; } diff --git a/includes/repositories/order-repository.php b/includes/repositories/order-repository.php index 08f5181b0f..8b8d453c12 100644 --- a/includes/repositories/order-repository.php +++ b/includes/repositories/order-repository.php @@ -94,6 +94,7 @@ public function get_latest_paid_featured_order_by_listing_id( int $listing_id ) ->select( 'd_order.created_at', 'd_order.expires_at' ) ->where( 'd_order.listing_id', $listing_id ) ->where( 'd_order.is_featured_listing', 1 ) + ->where( 'd_order.ref_type', 'featured_listing' ) ->where( 'd_order.status', OrderStatus::PAID ) ->order_by_desc( 'd_order.id' ) ->first(); From 6c4a2a482c4067309ece39f529ff240c7bd7344b Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Wed, 19 Aug 2026 20:20:27 +0600 Subject: [PATCH 10/19] fix(featured-listing): set expiration on partial paid updates --- .../class-featured-listing-checkout.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index c7c53f29f5..097087f308 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -31,7 +31,7 @@ public function register_hooks() { add_action( 'directorist_checkout_table', [$this, 'handle_checkout_table'], 10, 4 ); add_filter( 'directorist_checkout_subtotal', [$this, 'handle_checkout_subtotal'], 10, 3 ); add_action( 'directorist_checkout_create_order', [$this, 'handle_checkout_create_order'], 10, 3 ); - add_action( 'directorist_before_order_update', [$this, 'handle_before_order_update'] ); + add_action( 'directorist_before_order_update', [$this, 'handle_before_order_update'], 10, 2 ); add_filter( 'directorist_checkout_product_name', [$this, 'handle_checkout_product_name'], 10, 2 ); add_filter( 'directorist_ajax_listing_submission_response', [ $this, 'handle_listing_submission_response_data' ], 10, 2 ); add_filter( 'directorist_listing_update_args_after_preview', [ $this, 'handle_listing_status_after_preview' ], 10, 2 ); @@ -134,21 +134,20 @@ public function handle_checkout_create_order( OrderDTO $dto, string $checkout_ty $dto->set_listing_id( $request->get_param( 'listing_id' ) )->set_is_featured_listing( 1 )->set_ref_type( self::CHECKOUT_TYPE )->set_amount( $amount )->set_sub_total( $amount ); } - public function handle_before_order_update( OrderDTO $dto ) { - if ( ! $this->is_featured_order( $dto ) ) { + public function handle_before_order_update( OrderDTO $dto, $old_order ) { + if ( ! $dto->is_initialized( 'status' ) || $dto->get_status() !== Status::PAID ) { return; } - if ( ! $dto->is_initialized( 'status' ) || $dto->get_status() !== Status::PAID ) { + if ( empty( $old_order->is_featured_listing ) + || self::CHECKOUT_TYPE !== ( $old_order->ref_type ?? null ) + || ! empty( $old_order->expires_at ) + ) { return; } - $order = directorist_get_order_by_id( $dto->get_id() ); - - if ( $order->is_featured_listing && ! $order->expires_at ) { - $featured_days = get_directorist_option( 'featured_listing_time', 30 ); - $dto->set_expires_at( directorist_now()->add_days( $featured_days ) ); - } + $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); + $dto->set_expires_at( directorist_now()->add_days( $featured_days ) ); } public function handle_after_order_update( OrderDTO $dto ) { From 7bf7b67a80ccc39379ed1938221077ed9c211981 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 20 Aug 2026 10:59:26 +0600 Subject: [PATCH 11/19] fix(featured-listing): refresh expiration on paid updates --- includes/classes/class-featured-listing-checkout.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index 097087f308..61c5fd9e8a 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -141,7 +141,6 @@ public function handle_before_order_update( OrderDTO $dto, $old_order ) { if ( empty( $old_order->is_featured_listing ) || self::CHECKOUT_TYPE !== ( $old_order->ref_type ?? null ) - || ! empty( $old_order->expires_at ) ) { return; } From 4dda2b3458ae41f7ba5a326a2cf45f1698818ddb Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 20 Aug 2026 14:42:44 +0600 Subject: [PATCH 12/19] fix(featured-listing): persist expiration after listing update --- .../class-featured-listing-checkout.php | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index 61c5fd9e8a..9110b22653 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -31,7 +31,6 @@ public function register_hooks() { add_action( 'directorist_checkout_table', [$this, 'handle_checkout_table'], 10, 4 ); add_filter( 'directorist_checkout_subtotal', [$this, 'handle_checkout_subtotal'], 10, 3 ); add_action( 'directorist_checkout_create_order', [$this, 'handle_checkout_create_order'], 10, 3 ); - add_action( 'directorist_before_order_update', [$this, 'handle_before_order_update'], 10, 2 ); add_filter( 'directorist_checkout_product_name', [$this, 'handle_checkout_product_name'], 10, 2 ); add_filter( 'directorist_ajax_listing_submission_response', [ $this, 'handle_listing_submission_response_data' ], 10, 2 ); add_filter( 'directorist_listing_update_args_after_preview', [ $this, 'handle_listing_status_after_preview' ], 10, 2 ); @@ -134,21 +133,6 @@ public function handle_checkout_create_order( OrderDTO $dto, string $checkout_ty $dto->set_listing_id( $request->get_param( 'listing_id' ) )->set_is_featured_listing( 1 )->set_ref_type( self::CHECKOUT_TYPE )->set_amount( $amount )->set_sub_total( $amount ); } - public function handle_before_order_update( OrderDTO $dto, $old_order ) { - if ( ! $dto->is_initialized( 'status' ) || $dto->get_status() !== Status::PAID ) { - return; - } - - if ( empty( $old_order->is_featured_listing ) - || self::CHECKOUT_TYPE !== ( $old_order->ref_type ?? null ) - ) { - return; - } - - $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); - $dto->set_expires_at( directorist_now()->add_days( $featured_days ) ); - } - public function handle_after_order_update( OrderDTO $dto ) { if ( ! $this->is_featured_order( $dto ) ) { return; @@ -177,11 +161,13 @@ public function handle_after_order_update( OrderDTO $dto ) { } private function updated_listing_expiration( $listing, OrderDTO $order ) { - if ( ! $order->is_initialized( 'expires_at' ) || get_post_meta( $listing->ID, '_never_expire', true ) ) { + if ( get_post_meta( $listing->ID, '_never_expire', true ) ) { return; } - $order_expiration = $order->get_expires_at(); + $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); + $order_expiration = directorist_now()->add_days( $featured_days ); + $listing_expiration = get_post_meta( $listing->ID, '_expiry_date', true ); $listing_expiration_date = $listing_expiration ? date_create_from_format( 'Y-m-d H:i:s', $listing_expiration, wp_timezone() ) @@ -194,6 +180,9 @@ private function updated_listing_expiration( $listing, OrderDTO $order ) { return; } + $order->set_expires_at( $order_expiration ); + directorist_order_repository()->silent_update( $order ); + update_post_meta( $listing->ID, '_expiry_date', $order_expiration->format( 'Y-m-d H:i:s' ) ); } From 8255e7d9c9cefdf59a7d44a32bf77fcde280b9d5 Mon Sep 17 00:00:00 2001 From: Armanul46 <47377178+Armanul46@users.noreply.github.com> Date: Thu, 27 Aug 2026 10:42:00 +0600 Subject: [PATCH 13/19] readme update --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index d01e6a3bc3..5a19c4f12a 100644 --- a/readme.txt +++ b/readme.txt @@ -307,6 +307,7 @@ Directorist comes with an AI-powered directory builder. Use the Create with AI o **Fixed** - Author profile links missing directory type slug in multi-directory listings. (#2957) +- Featured listing expiration sync issue. (#2969) = 8.9.3 - Aug 9, 2026 = From 34f369a438eaf28d0f90cb230bda4f1b164e08f7 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 27 Aug 2026 12:14:52 +0600 Subject: [PATCH 14/19] fix(featured-listing): persist order expiration before early return --- includes/classes/class-featured-listing-checkout.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index 9110b22653..07cb3329e5 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -173,6 +173,10 @@ private function updated_listing_expiration( $listing, OrderDTO $order ) { ? date_create_from_format( 'Y-m-d H:i:s', $listing_expiration, wp_timezone() ) : false; + + $order->set_expires_at( $order_expiration ); + directorist_order_repository()->silent_update( $order ); + if ( $listing_expiration_date && $listing_expiration === $listing_expiration_date->format( 'Y-m-d H:i:s' ) && $listing_expiration_date->getTimestamp() >= $order_expiration->getTimestamp() @@ -180,9 +184,6 @@ private function updated_listing_expiration( $listing, OrderDTO $order ) { return; } - $order->set_expires_at( $order_expiration ); - directorist_order_repository()->silent_update( $order ); - update_post_meta( $listing->ID, '_expiry_date', $order_expiration->format( 'Y-m-d H:i:s' ) ); } From 33f08652ae0107afa54013003ad29eabdce214ac Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 27 Aug 2026 12:47:39 +0600 Subject: [PATCH 15/19] fix(payment): resolve prepaid and featured expiry handling --- .../class-featured-listing-checkout.php | 27 ++++++++++------ .../Version1/class-orders-controller.php | 32 +++++++++++++++++-- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index 07cb3329e5..691567601d 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -143,13 +143,15 @@ public function handle_after_order_update( OrderDTO $dto ) { } if ( Status::PAID === $dto->get_status() ) { + $order_expiration = $this->refresh_order_expiration( $dto ); + directorist_set_listing_featured( $dto->get_listing_id(), true ); // Publish the listing if it's pending $listing = get_post( $dto->get_listing_id() ); if ( $listing ) { - $this->updated_listing_expiration( $listing, $dto ); + $this->update_listing_expiration( $listing, $order_expiration ); } if ( $listing && 'publish' !== $listing->post_status ) { @@ -160,23 +162,30 @@ public function handle_after_order_update( OrderDTO $dto ) { } } - private function updated_listing_expiration( $listing, OrderDTO $order ) { + private function refresh_order_expiration( OrderDTO $order ) { + $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); + $order_expiration = directorist_now()->add_days( $featured_days ); + + $order->set_expires_at( $order_expiration ); + $expiration_update = ( new OrderDTO() ) + ->set_id( $order->get_id() ) + ->set_expires_at( $order_expiration ); + + directorist_order_repository()->silent_update( $expiration_update ); + + return $order_expiration; + } + + private function update_listing_expiration( $listing, $order_expiration ) { if ( get_post_meta( $listing->ID, '_never_expire', true ) ) { return; } - $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); - $order_expiration = directorist_now()->add_days( $featured_days ); - $listing_expiration = get_post_meta( $listing->ID, '_expiry_date', true ); $listing_expiration_date = $listing_expiration ? date_create_from_format( 'Y-m-d H:i:s', $listing_expiration, wp_timezone() ) : false; - - $order->set_expires_at( $order_expiration ); - directorist_order_repository()->silent_update( $order ); - if ( $listing_expiration_date && $listing_expiration === $listing_expiration_date->format( 'Y-m-d H:i:s' ) && $listing_expiration_date->getTimestamp() >= $order_expiration->getTimestamp() diff --git a/includes/rest-api/Version1/class-orders-controller.php b/includes/rest-api/Version1/class-orders-controller.php index afbea5c885..2b805adc77 100644 --- a/includes/rest-api/Version1/class-orders-controller.php +++ b/includes/rest-api/Version1/class-orders-controller.php @@ -12,6 +12,7 @@ use Directorist\DTO\Order\DTO as OrderDTO; use Directorist\DTO\Payment\DTO as PaymentDTO; use Directorist\Enums\Order\Status as OrderStatus; +use Directorist\Enums\Payment\Status as PaymentStatus; use WP_Error; use WP_REST_Request; use WP_REST_Server; @@ -140,7 +141,8 @@ public function create_item( $request ) { $dto = apply_filters( 'directorist_rest_legacy_order_create_dto', $dto, $request ); - $order_id = directorist_order_repository()->create( $dto ); + $order_repository = directorist_order_repository(); + $order_id = $order_repository->create( $dto ); if ( ! $order_id ) { return new WP_Error( 'invalid_order', __( 'Unable to create order.', 'directorist' ), array( 'status' => 400 ) ); @@ -153,11 +155,21 @@ public function create_item( $request ) { ->set_order_id( $order_id ) ->set_amount( $amount ) ->set_currency( $currency ) - ->set_status( $this->legacy_status_to_current_status( $legacy_status ) ) + ->set_status( $this->legacy_payment_status_to_current_status( $legacy_status ) ) ->set_method( $gateway ) ->set_transaction_id( $transaction_id ? $transaction_id : null ); - directorist_payment_repository()->create( apply_filters( 'directorist_rest_legacy_order_create_payment_dto', $payment_dto, $request, $dto ) ); + $payment_dto = apply_filters( 'directorist_rest_legacy_order_create_payment_dto', $payment_dto, $request, $dto ); + $payment_repository = directorist_payment_repository(); + $payment_id = $payment_repository->create( $payment_dto ); + $payment = $payment_repository->get_last_payment( $order_id ); + + if ( ! $payment_id || ! $payment || $payment->status !== $payment_dto->get_status() ) { + $payment_repository->delete_by( 'order_id', $order_id ); + $order_repository->delete_by_id( $order_id ); + + return new WP_Error( 'rest_payment_create_failed', __( 'Unable to create payment.', 'directorist' ), array( 'status' => 500 ) ); + } } do_action( 'atbdp_order_created', $order_id, $listing_id ); @@ -315,6 +327,20 @@ protected function legacy_status_to_current_status( string $status ): string { return $map[ $status ] ?? OrderStatus::PENDING; } + protected function legacy_payment_status_to_current_status( string $status ): string { + $map = array( + 'created' => PaymentStatus::PENDING, + 'pending' => PaymentStatus::PENDING, + 'prepaid' => PaymentStatus::PAID, + 'completed' => PaymentStatus::PAID, + 'failed' => PaymentStatus::FAILED, + 'cancelled' => PaymentStatus::CANCELLED, + 'refunded' => PaymentStatus::REFUNDED, + ); + + return $map[ $status ] ?? PaymentStatus::PENDING; + } + protected function current_status_to_legacy_status( string $status ): string { $map = array( OrderStatus::PENDING => 'pending', From 0e9bbf13c41f5877b25ce0215705823df8d2c907 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 27 Aug 2026 15:08:34 +0600 Subject: [PATCH 16/19] fix(payment): preserve prepaid order lifecycle --- includes/repositories/payment-repository.php | 26 ++++++++++++++++--- .../Version1/class-orders-controller.php | 13 +++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/includes/repositories/payment-repository.php b/includes/repositories/payment-repository.php index d669148943..0daaf82e46 100644 --- a/includes/repositories/payment-repository.php +++ b/includes/repositories/payment-repository.php @@ -9,6 +9,7 @@ use Directorist\Utils\Database\Query\Builder; use Directorist\DTO\Payment\DTO; use Directorist\Enums\Order\Status as OrderStatus; +use Directorist\Enums\Payment\Status as PaymentStatus; use Directorist\DBModels\Payment; class PaymentRepository extends Repository { @@ -33,15 +34,34 @@ public function get( $order_id ) { * @return int */ public function create( \Directorist\Utils\DTO $dto ) { - $payment_id = parent::create( $dto ); + $payment_id = $this->create_without_order_update( $dto ); + $payment = $payment_id ? $this->get_last_payment( $dto->get_order_id() ) : null; + + if ( ! $payment || (int) $payment->id !== $payment_id || $payment->status !== $dto->get_status() ) { + return 0; + } - if ( $dto->is_initialized( 'status' ) && $dto->get_status() === OrderStatus::PAID ) { + if ( $dto->is_initialized( 'status' ) && $dto->get_status() === PaymentStatus::PAID ) { $this->order_repository->update_status( $dto->get_order_id(), OrderStatus::PAID ); } return $payment_id; } + /** + * Create a payment without synchronizing the related order status. + * + * @param \Directorist\Utils\DTO $dto Payment data. + * @return int Payment ID, or zero when the insert fails. + */ + public function create_without_order_update( \Directorist\Utils\DTO $dto ) { + global $wpdb; + + $payment_id = parent::create( $dto ); + + return $wpdb->last_error ? 0 : $payment_id; + } + public function update_status_by_order_id( int $order_id, string $status ): int { return $this->get_query_builder() ->where( 'order_id', $order_id ) @@ -69,4 +89,4 @@ public function to_dto( $payment ) { return $dto; } -} \ No newline at end of file +} diff --git a/includes/rest-api/Version1/class-orders-controller.php b/includes/rest-api/Version1/class-orders-controller.php index 2b805adc77..054940c981 100644 --- a/includes/rest-api/Version1/class-orders-controller.php +++ b/includes/rest-api/Version1/class-orders-controller.php @@ -161,15 +161,19 @@ public function create_item( $request ) { $payment_dto = apply_filters( 'directorist_rest_legacy_order_create_payment_dto', $payment_dto, $request, $dto ); $payment_repository = directorist_payment_repository(); - $payment_id = $payment_repository->create( $payment_dto ); + $payment_id = $payment_repository->create_without_order_update( $payment_dto ); $payment = $payment_repository->get_last_payment( $order_id ); - if ( ! $payment_id || ! $payment || $payment->status !== $payment_dto->get_status() ) { + if ( ! $payment_id || ! $payment || (int) $payment->id !== $payment_id || $payment->status !== $payment_dto->get_status() ) { $payment_repository->delete_by( 'order_id', $order_id ); $order_repository->delete_by_id( $order_id ); return new WP_Error( 'rest_payment_create_failed', __( 'Unable to create payment.', 'directorist' ), array( 'status' => 500 ) ); } + + if ( OrderStatus::PAID === $dto->get_status() ) { + $order_repository->update_status( $order_id, OrderStatus::PAID ); + } } do_action( 'atbdp_order_created', $order_id, $listing_id ); @@ -270,6 +274,9 @@ protected function get_order( int $order_id ) { protected function prepare_legacy_order_data( $order, WP_REST_Request $request ): array { $payment = ! empty( $order->payment ) ? $order->payment : null; $plan_id = $this->get_plan_id( $order ); + $status = OrderStatus::PREPAID === ( $order->status ?? '' ) + ? $order->status + : ( $payment->status ?? $order->status ?? '' ); $data = array( 'id' => (int) $order->id, @@ -285,7 +292,7 @@ protected function prepare_legacy_order_data( $order, WP_REST_Request $request ) 'remaining_featured_listings' => 0, 'amount' => round( (float) ( $order->amount ?? 0 ), 2 ), 'currency' => (string) ( $order->currency ?? '' ), - 'payment_status' => $this->current_status_to_legacy_status( $payment->status ?? $order->status ?? '' ), + 'payment_status' => $this->current_status_to_legacy_status( $status ), 'payment_gateway' => (string) ( $payment->method ?? '' ), 'transaction_id' => (string) ( $payment->transaction_id ?? '' ), 'created_by' => 'web', From 3bc8e8a6e09c569aec42d8809adf7f5f6cd10b4b Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 27 Aug 2026 15:26:24 +0600 Subject: [PATCH 17/19] fix(payment): preserve entitlements and inserted payments --- .../class-featured-listing-checkout.php | 2 +- includes/repositories/order-repository.php | 29 +++++++++++++++++++ includes/repositories/payment-repository.php | 2 +- .../Version1/class-orders-controller.php | 2 +- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index 691567601d..cd52ce17f0 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -157,7 +157,7 @@ public function handle_after_order_update( OrderDTO $dto ) { if ( $listing && 'publish' !== $listing->post_status ) { directorist_set_listing_status( $dto->get_listing_id(), 'publish' ); } - } else { + } elseif ( ! directorist_order_repository()->listing_has_active_featured_entitlement( $dto->get_listing_id() ) ) { directorist_set_listing_featured( $dto->get_listing_id(), false ); } } diff --git a/includes/repositories/order-repository.php b/includes/repositories/order-repository.php index 8b8d453c12..c46779507b 100644 --- a/includes/repositories/order-repository.php +++ b/includes/repositories/order-repository.php @@ -89,6 +89,35 @@ public function listing_has_paid_featured_order( int $listing_id ): bool { return $order ? true : false; } + public function listing_has_active_featured_entitlement( int $listing_id ): bool { + $orders = $this->get_query_builder() + ->select( 'd_order.ref_type', 'd_order.created_at', 'd_order.expires_at' ) + ->where( 'd_order.listing_id', $listing_id ) + ->where( 'd_order.is_featured_listing', 1 ) + ->where( 'd_order.status', OrderStatus::PAID ) + ->get(); + + $current_time = directorist_now()->getTimestamp(); + $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); + $is_active = false; + + foreach ( $orders as $order ) { + if ( ! empty( $order->expires_at ) ) { + $is_active = ( new DateTime( $order->expires_at ) )->getTimestamp() > $current_time; + } elseif ( 'featured_listing' === $order->ref_type ) { + $is_active = ( new DateTime( $order->created_at ) )->add_days( $featured_days )->getTimestamp() > $current_time; + } else { + $is_active = true; + } + + if ( $is_active ) { + break; + } + } + + return (bool) apply_filters( 'directorist_listing_has_active_featured_entitlement', $is_active, $listing_id, $orders ); + } + public function get_latest_paid_featured_order_by_listing_id( int $listing_id ) { return $this->get_query_builder() ->select( 'd_order.created_at', 'd_order.expires_at' ) diff --git a/includes/repositories/payment-repository.php b/includes/repositories/payment-repository.php index 0daaf82e46..bb87f503ad 100644 --- a/includes/repositories/payment-repository.php +++ b/includes/repositories/payment-repository.php @@ -35,7 +35,7 @@ public function get( $order_id ) { */ public function create( \Directorist\Utils\DTO $dto ) { $payment_id = $this->create_without_order_update( $dto ); - $payment = $payment_id ? $this->get_last_payment( $dto->get_order_id() ) : null; + $payment = $payment_id ? $this->get_by_id( $payment_id ) : null; if ( ! $payment || (int) $payment->id !== $payment_id || $payment->status !== $dto->get_status() ) { return 0; diff --git a/includes/rest-api/Version1/class-orders-controller.php b/includes/rest-api/Version1/class-orders-controller.php index 054940c981..f2f55bf2c4 100644 --- a/includes/rest-api/Version1/class-orders-controller.php +++ b/includes/rest-api/Version1/class-orders-controller.php @@ -162,7 +162,7 @@ public function create_item( $request ) { $payment_dto = apply_filters( 'directorist_rest_legacy_order_create_payment_dto', $payment_dto, $request, $dto ); $payment_repository = directorist_payment_repository(); $payment_id = $payment_repository->create_without_order_update( $payment_dto ); - $payment = $payment_repository->get_last_payment( $order_id ); + $payment = $payment_id ? $payment_repository->get_by_id( $payment_id ) : null; if ( ! $payment_id || ! $payment || (int) $payment->id !== $payment_id || $payment->status !== $payment_dto->get_status() ) { $payment_repository->delete_by( 'order_id', $order_id ); From 496d25e396f7b9f9f5d8d4672ae91e078fd6a988 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 27 Aug 2026 15:43:16 +0600 Subject: [PATCH 18/19] fix(featured-listing): preserve entitlement expiration --- includes/classes/class-cron.php | 19 ++++++------------- .../class-featured-listing-checkout.php | 14 ++++++++++---- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/includes/classes/class-cron.php b/includes/classes/class-cron.php index e1f84d1597..282db94048 100644 --- a/includes/classes/class-cron.php +++ b/includes/classes/class-cron.php @@ -103,9 +103,7 @@ private function featured_listing_followup() { } if ( directorist_is_monetization_enabled() && directorist_is_featured_listing_enabled() ) { - $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); - $orders = directorist_order_repository(); - $current_time = directorist_now()->getTimestamp(); + $orders = directorist_order_repository(); // Define the query $args = [ 'post_type' => ATBDP_POST_TYPE, @@ -126,20 +124,15 @@ private function featured_listing_followup() { // Start the Loop if ( $listings->found_posts ) { foreach ( $listings->posts as $listing ) { - $order = $orders->get_latest_paid_featured_order_by_listing_id( $listing->ID ); + $has_paid_order = $orders->listing_has_paid_featured_order( $listing->ID ); + $has_active_entitlement = $orders->listing_has_active_featured_entitlement( $listing->ID ); - if ( ! $order ) { + if ( ! $has_paid_order || $has_active_entitlement ) { continue; } - $expiration = ! empty( $order->expires_at ) - ? new \Directorist\Helpers\DateTime( $order->expires_at ) - : ( new \Directorist\Helpers\DateTime( $order->created_at ) )->add_days( $featured_days ); - - if ( $expiration->getTimestamp() <= $current_time ) { - do_action( 'atbdp_listing_featured_to_general', $listing->ID ); - update_post_meta( $listing->ID, '_featured', '' ); - } + do_action( 'atbdp_listing_featured_to_general', $listing->ID ); + update_post_meta( $listing->ID, '_featured', '' ); } } } diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index cd52ce17f0..6e7b77048c 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -26,7 +26,7 @@ public function register_hooks() { } add_filter( 'directorist_checkout_types', [$this, 'add_checkout_type'] ); - add_action( 'directorist_after_order_update', [$this, 'handle_after_order_update'] ); + add_action( 'directorist_after_order_update', [$this, 'handle_after_order_update'], 10, 2 ); add_filter( 'directorist_checkout_validation', [$this, 'validate_checkout'], 10, 2 ); add_action( 'directorist_checkout_table', [$this, 'handle_checkout_table'], 10, 4 ); add_filter( 'directorist_checkout_subtotal', [$this, 'handle_checkout_subtotal'], 10, 3 ); @@ -133,7 +133,7 @@ public function handle_checkout_create_order( OrderDTO $dto, string $checkout_ty $dto->set_listing_id( $request->get_param( 'listing_id' ) )->set_is_featured_listing( 1 )->set_ref_type( self::CHECKOUT_TYPE )->set_amount( $amount )->set_sub_total( $amount ); } - public function handle_after_order_update( OrderDTO $dto ) { + public function handle_after_order_update( OrderDTO $dto, $old_order = null ) { if ( ! $this->is_featured_order( $dto ) ) { return; } @@ -143,14 +143,16 @@ public function handle_after_order_update( OrderDTO $dto ) { } if ( Status::PAID === $dto->get_status() ) { - $order_expiration = $this->refresh_order_expiration( $dto ); + $order_expiration = $this->is_paid_transition( $old_order ) + ? $this->refresh_order_expiration( $dto ) + : null; directorist_set_listing_featured( $dto->get_listing_id(), true ); // Publish the listing if it's pending $listing = get_post( $dto->get_listing_id() ); - if ( $listing ) { + if ( $listing && $order_expiration ) { $this->update_listing_expiration( $listing, $order_expiration ); } @@ -162,6 +164,10 @@ public function handle_after_order_update( OrderDTO $dto ) { } } + private function is_paid_transition( $old_order ): bool { + return ! $old_order || ! isset( $old_order->status ) || Status::PAID !== $old_order->status; + } + private function refresh_order_expiration( OrderDTO $order ) { $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); $order_expiration = directorist_now()->add_days( $featured_days ); From 46e6422c0ee2bb3550747fbf11df185d15631947 Mon Sep 17 00:00:00 2001 From: Syed Galib Ahmed Date: Thu, 27 Aug 2026 15:56:41 +0600 Subject: [PATCH 19/19] fix(featured-listing): initialize missing paid expiry --- includes/classes/class-featured-listing-checkout.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/classes/class-featured-listing-checkout.php b/includes/classes/class-featured-listing-checkout.php index 6e7b77048c..c1ea7b3007 100644 --- a/includes/classes/class-featured-listing-checkout.php +++ b/includes/classes/class-featured-listing-checkout.php @@ -143,7 +143,7 @@ public function handle_after_order_update( OrderDTO $dto, $old_order = null ) { } if ( Status::PAID === $dto->get_status() ) { - $order_expiration = $this->is_paid_transition( $old_order ) + $order_expiration = $this->should_refresh_order_expiration( $dto, $old_order ) ? $this->refresh_order_expiration( $dto ) : null; @@ -168,6 +168,10 @@ private function is_paid_transition( $old_order ): bool { return ! $old_order || ! isset( $old_order->status ) || Status::PAID !== $old_order->status; } + private function should_refresh_order_expiration( OrderDTO $order, $old_order ): bool { + return $this->is_paid_transition( $old_order ) || ! $order->is_initialized( 'expires_at' ); + } + private function refresh_order_expiration( OrderDTO $order ) { $featured_days = (int) get_directorist_option( 'featured_listing_time', 30 ); $order_expiration = directorist_now()->add_days( $featured_days );