product-purchased.php
259 lines
| 1 | <?php |
| 2 | /** |
| 3 | * ProductPurchased. |
| 4 | * php version 5.6 |
| 5 | * |
| 6 | * @category ProductPurchased |
| 7 | * @package SureTriggers |
| 8 | * @author BSF <username@example.com> |
| 9 | * @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3 |
| 10 | * @link https://www.brainstormforce.com/ |
| 11 | * @since 1.0.0 |
| 12 | */ |
| 13 | |
| 14 | namespace SureTriggers\Integrations\StoreEngine\Triggers; |
| 15 | |
| 16 | use SureTriggers\Controllers\AutomationController; |
| 17 | use SureTriggers\Traits\SingletonLoader; |
| 18 | |
| 19 | if ( ! class_exists( 'ProductPurchased' ) ) : |
| 20 | |
| 21 | /** |
| 22 | * ProductPurchased |
| 23 | * |
| 24 | * @category ProductPurchased |
| 25 | * @package SureTriggers |
| 26 | * @author BSF <username@example.com> |
| 27 | * @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3 |
| 28 | * @link https://www.brainstormforce.com/ |
| 29 | * @since 1.0.0 |
| 30 | * |
| 31 | * @psalm-suppress UndefinedTrait |
| 32 | */ |
| 33 | class ProductPurchased { |
| 34 | |
| 35 | /** |
| 36 | * Integration type. |
| 37 | * |
| 38 | * @var string |
| 39 | */ |
| 40 | public $integration = 'StoreEngine'; |
| 41 | |
| 42 | /** |
| 43 | * Trigger name. |
| 44 | * |
| 45 | * @var string |
| 46 | */ |
| 47 | public $trigger = 'se_product_purchased'; |
| 48 | |
| 49 | use SingletonLoader; |
| 50 | |
| 51 | /** |
| 52 | * Constructor |
| 53 | * |
| 54 | * @since 1.0.0 |
| 55 | */ |
| 56 | public function __construct() { |
| 57 | add_filter( 'sure_trigger_register_trigger', [ $this, 'register' ] ); |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Register action. |
| 62 | * |
| 63 | * @param array $triggers trigger data. |
| 64 | * @return array |
| 65 | */ |
| 66 | public function register( $triggers ) { |
| 67 | $triggers[ $this->integration ][ $this->trigger ] = [ |
| 68 | 'label' => __( 'Product Purchased', 'suretriggers' ), |
| 69 | 'action' => 'se_product_purchased', |
| 70 | 'common_action' => 'storeengine/order_status_completed', |
| 71 | 'function' => [ $this, 'trigger_listener' ], |
| 72 | 'priority' => 10, |
| 73 | 'accepted_args' => 3, |
| 74 | ]; |
| 75 | |
| 76 | return $triggers; |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Trigger listener for order status transition. |
| 81 | * |
| 82 | * @param int $order_id The ID of the order. |
| 83 | * @param object $order The StoreEngine order object. |
| 84 | * @param array $status_transition Array containing 'from', 'to', and 'order' status transition data. |
| 85 | * |
| 86 | * @return void |
| 87 | */ |
| 88 | public function trigger_listener( $order_id, $order, $status_transition ) { |
| 89 | |
| 90 | if ( empty( $order_id ) || empty( $order ) ) { |
| 91 | return; |
| 92 | } |
| 93 | |
| 94 | // Verify order object has required methods. |
| 95 | if ( ! method_exists( $order, 'get_items' ) || ! method_exists( $order, 'get_customer_id' ) || ! method_exists( $order, 'get_status' ) || ! method_exists( $order, 'get_total' ) || ! method_exists( $order, 'get_currency' ) ) { |
| 96 | return; |
| 97 | } |
| 98 | |
| 99 | // Get order items. |
| 100 | $items = $order->get_items(); |
| 101 | if ( empty( $items ) || ! is_array( $items ) ) { |
| 102 | return; |
| 103 | } |
| 104 | $context = []; |
| 105 | |
| 106 | // Process each product in the order. |
| 107 | foreach ( $items as $item ) { |
| 108 | // Verify item has required methods. |
| 109 | if ( ! is_object( $item ) || ! method_exists( $item, 'get_product_id' ) || ! method_exists( $item, 'get_price_id' ) ) { |
| 110 | continue; |
| 111 | } |
| 112 | |
| 113 | $product_id = $item->get_product_id(); |
| 114 | |
| 115 | |
| 116 | // Get product details. |
| 117 | $product = get_post( $product_id ); |
| 118 | if ( ! $product ) { |
| 119 | continue; |
| 120 | } |
| 121 | |
| 122 | // Get user details. |
| 123 | $user_id = $order->get_customer_id(); |
| 124 | $user = get_userdata( $user_id ); |
| 125 | |
| 126 | // Build context matching search_se_triggers_last_data structure. |
| 127 | $context = [ |
| 128 | // Product information (flat structure). |
| 129 | 'id' => $product_id, |
| 130 | 'post_author' => $product->post_author, |
| 131 | 'post_date' => $product->post_date, |
| 132 | 'post_date_gmt' => $product->post_date_gmt, |
| 133 | 'post_content' => $product->post_content, |
| 134 | 'post_title' => $product->post_title, |
| 135 | 'post_excerpt' => $product->post_excerpt, |
| 136 | 'post_status' => $product->post_status, |
| 137 | 'comment_status' => $product->comment_status, |
| 138 | 'ping_status' => $product->ping_status, |
| 139 | 'post_password' => $product->post_password, |
| 140 | 'post_name' => $product->post_name, |
| 141 | 'to_ping' => $product->to_ping, |
| 142 | 'pinged' => $product->pinged, |
| 143 | 'post_modified' => $product->post_modified, |
| 144 | 'post_modified_gmt' => $product->post_modified_gmt, |
| 145 | 'post_content_filtered' => $product->post_content_filtered, |
| 146 | 'post_parent' => $product->post_parent, |
| 147 | 'guid' => $product->guid, |
| 148 | 'menu_order' => $product->menu_order, |
| 149 | 'post_type' => $product->post_type, |
| 150 | 'post_mime_type' => $product->post_mime_type, |
| 151 | 'comment_count' => $product->comment_count, |
| 152 | 'filter' => 'raw', |
| 153 | 'featured_image' => get_post_thumbnail_id( $product_id ) ? get_post_thumbnail_id( $product_id ) : null, |
| 154 | 'permalink' => get_permalink( $product_id ), |
| 155 | |
| 156 | // User information (flat structure). |
| 157 | 'wp_user_id' => $user_id, |
| 158 | 'user_login' => $user ? $user->user_login : '', |
| 159 | 'display_name' => $user ? $user->display_name : '', |
| 160 | 'user_firstname' => $user ? get_user_meta( $user_id, 'first_name', true ) : '', |
| 161 | 'user_lastname' => $user ? get_user_meta( $user_id, 'last_name', true ) : '', |
| 162 | 'user_email' => $user ? $user->user_email : '', |
| 163 | 'user_registered' => $user ? $user->user_registered : '', |
| 164 | 'user_role' => $user ? $user->roles : [], |
| 165 | |
| 166 | // Keep post ID for backwards compatibility. |
| 167 | 'post' => $product_id, |
| 168 | ]; |
| 169 | |
| 170 | // Get all custom metas for the product - check with underscore prefix. |
| 171 | $custom_metas = []; |
| 172 | $all_meta = get_post_meta( $product_id ); |
| 173 | |
| 174 | // Filter StoreEngine specific metas (they start with underscore). |
| 175 | if ( is_array( $all_meta ) ) { |
| 176 | foreach ( $all_meta as $meta_key => $meta_value ) { |
| 177 | if ( is_string( $meta_key ) && strpos( $meta_key, '_storeengine_' ) === 0 ) { |
| 178 | // Remove underscore prefix for cleaner keys. |
| 179 | $clean_key = ltrim( $meta_key, '_' ); |
| 180 | if ( is_array( $meta_value ) && isset( $meta_value[0] ) ) { |
| 181 | $custom_metas[ $clean_key ] = maybe_unserialize( $meta_value[0] ); |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | $context['product_id'] = $product_id; |
| 188 | $context['custom_metas'] = $custom_metas; |
| 189 | |
| 190 | // Check if product has membership integration using StoreEngine helper. |
| 191 | $price_id = $item->get_price_id(); |
| 192 | |
| 193 | if ( class_exists( '\StoreEngine\Utils\Helper' ) ) { |
| 194 | // Use StoreEngine's helper function to get integration data. |
| 195 | $membership_integration = \StoreEngine\Utils\Helper::get_integration_repository_by_price_id( 'storeengine/membership-addon', $price_id ); |
| 196 | } else { |
| 197 | // Fallback if StoreEngine's helper is not available. |
| 198 | $membership_integration = null; |
| 199 | } |
| 200 | |
| 201 | // Add membership details if this is a membership product. |
| 202 | if ( $membership_integration && ! empty( $membership_integration->integration ) ) { |
| 203 | $membership_plan_id = $membership_integration->integration->get_integration_id(); |
| 204 | $access_group = get_post( $membership_plan_id ); |
| 205 | |
| 206 | if ( $access_group && 'storeengine_groups' === $access_group->post_type ) { |
| 207 | // Add membership information to context - matching search_se_triggers_last_data structure. |
| 208 | $context['membership_plan_id'] = $membership_plan_id; |
| 209 | $context['membership_plan_name'] = $access_group->post_title; |
| 210 | $context['membership_plan_slug'] = $access_group->post_name; |
| 211 | |
| 212 | // Get membership meta data. |
| 213 | $membership_meta = [ |
| 214 | 'user_roles' => get_post_meta( $membership_plan_id, '_storeengine_membership_user_roles', true ), |
| 215 | 'content_protects' => get_post_meta( $membership_plan_id, '_storeengine_membership_content_protect_types', true ), |
| 216 | 'expiration' => get_post_meta( $membership_plan_id, '_storeengine_membership_expiration', true ), |
| 217 | 'priority' => get_post_meta( $membership_plan_id, '_storeengine_membership_priority', true ), |
| 218 | 'excluded_items' => get_post_meta( $membership_plan_id, '_storeengine_membership_content_protect_excluded_items', true ), |
| 219 | ]; |
| 220 | |
| 221 | $context['membership'] = array_filter( $membership_meta ); |
| 222 | |
| 223 | // Add user membership data if available. |
| 224 | if ( $user_id ) { |
| 225 | $user_membership_data = get_user_meta( $user_id, '_storeengine_user_membership_data', true ); |
| 226 | if ( is_array( $user_membership_data ) && isset( $user_membership_data[ $membership_plan_id ] ) ) { |
| 227 | $context['user_membership_data'] = $user_membership_data[ $membership_plan_id ]; |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | // Add order data for membership purchases - matching search_se_triggers_last_data. |
| 232 | $context['order'] = [ |
| 233 | 'order_id' => $order_id, |
| 234 | 'order_status' => $order->get_status(), |
| 235 | 'order_total' => $order->get_total(), |
| 236 | 'currency' => $order->get_currency(), |
| 237 | ]; |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | AutomationController::sure_trigger_handle_trigger( |
| 243 | [ |
| 244 | 'trigger' => $this->trigger, |
| 245 | 'context' => $context, |
| 246 | ] |
| 247 | ); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | /** |
| 252 | * Ignore false positive |
| 253 | * |
| 254 | * @psalm-suppress UndefinedMethod |
| 255 | */ |
| 256 | ProductPurchased::get_instance(); |
| 257 | |
| 258 | endif; |
| 259 |