JezK
Edit File: class-cartflows-pro-webhook.php
<?php /** * CartFlows Pro Webhook Module. * * Adds upsell/downsell/order bump webhook events to the free webhook system. * * @package CartFlows */ // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } if ( ! class_exists( 'Cartflows_Pro_Webhook' ) ) { /** * Class Cartflows_Pro_Webhook. */ class Cartflows_Pro_Webhook { /** * Instance * * @access private * @var object Class object. * @since 2.2.2 */ private static $instance; /** * Initiator * * @return object initialized object of class. */ public static function get_instance() { if ( ! isset( self::$instance ) ) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ public function __construct() { // Add Pro events to available events list. add_filter( 'cartflows_webhook_available_events', array( $this, 'add_pro_events' ) ); // Register Pro event hooks for upsell/downsell using CartFlows offer actions. add_action( 'cartflows_offer_accepted', array( $this, 'handle_offer_accepted' ), 10, 2 ); add_action( 'cartflows_offer_rejected', array( $this, 'handle_offer_rejected' ), 10, 2 ); // Order bump - detect accepted/skipped bumps when the order is created. add_action( 'woocommerce_checkout_order_processed', array( $this, 'handle_order_bump_events' ), 20, 3 ); } /** * Add Pro events to the available events list. * * @param array $events Existing events. * @return array */ public function add_pro_events( $events ) { $events['order_bump_accepted'] = __( 'Order Bump Accepted', 'cartflows-pro' ); $events['order_bump_skipped'] = __( 'Order Bump Skipped', 'cartflows-pro' ); $events['upsell_accepted'] = __( 'Upsell Accepted', 'cartflows-pro' ); $events['upsell_skipped'] = __( 'Upsell Skipped', 'cartflows-pro' ); $events['downsell_accepted'] = __( 'Downsell Accepted', 'cartflows-pro' ); $events['downsell_skipped'] = __( 'Downsell Skipped', 'cartflows-pro' ); return $events; } /** * Handle order bump events at order creation time. * * Compares configured bumps on the checkout step against accepted bump * items in the order to dispatch order_bump_accepted / order_bump_skipped. * * Hooked at priority 20 on woocommerce_checkout_order_processed so it * runs after the free plugin's order_created webhook (priority 10). * * @param int $order_id Order ID. * @param array $posted_data Posted checkout data. * @param WC_Order $order Order object. * @return void */ public function handle_order_bump_events( $order_id, $posted_data, $order ) { if ( ! $order instanceof WC_Order ) { return; } $flow_id = $order->get_meta( '_wcf_flow_id' ); if ( empty( $flow_id ) || ! is_numeric( $flow_id ) ) { return; // Not a CartFlows order. } $step_id = $order->get_meta( '_wcf_checkout_id' ); if ( empty( $step_id ) || ! is_numeric( $step_id ) ) { return; } // Get configured order bumps for this checkout step. $configured_bumps = get_post_meta( absint( $step_id ), 'wcf-order-bumps', true ); if ( empty( $configured_bumps ) || ! is_array( $configured_bumps ) ) { return; // No bumps configured on this checkout. } // Get accepted bump IDs from order meta (keys are the bump IDs). $bump_products = $order->get_meta( '_wcf_bump_products', true ); $accepted_ob_ids = ! empty( $bump_products ) && is_array( $bump_products ) ? array_keys( $bump_products ) : array(); // Iterate configured bumps and dispatch accepted/skipped events. foreach ( $configured_bumps as $bump ) { if ( empty( $bump['id'] ) ) { continue; } // Skip disabled bumps. if ( isset( $bump['status'] ) && empty( $bump['status'] ) ) { continue; } $bump_id = $bump['id']; $product_id = isset( $bump['product'] ) ? absint( $bump['product'] ) : 0; $product = $product_id ? wc_get_product( $product_id ) : null; $context = array( 'funnel_id' => absint( $flow_id ), 'step_id' => absint( $step_id ), 'order_id' => absint( $order_id ), 'order' => $order, 'bump_product' => array( 'id' => $product_id, 'name' => $product ? $product->get_name() : '', 'price' => $product ? $product->get_price() : '', 'ob_id' => $bump_id, ), ); if ( in_array( $bump_id, $accepted_ob_ids, true ) ) { wcf()->logger->log( 'Webhook Pro event: order_bump_accepted - Order ' . $order_id . ' Product ' . $product_id . ' OB ' . $bump_id ); Cartflows_Webhook_Dispatcher::get_instance()->dispatch( 'order_bump_accepted', $context ); } else { wcf()->logger->log( 'Webhook Pro event: order_bump_skipped - Order ' . $order_id . ' Product ' . $product_id . ' OB ' . $bump_id ); Cartflows_Webhook_Dispatcher::get_instance()->dispatch( 'order_bump_skipped', $context ); } } } /** * Handle offer accepted (upsell or downsell). * * Fired by cartflows_offer_accepted action. * * @param WC_Order $order Order object. * @param array $offer_product Offer product data. * @return void */ public function handle_offer_accepted( $order, $offer_product = array() ) { if ( ! $order instanceof WC_Order ) { return; } $step_id = isset( $offer_product['step_id'] ) ? absint( $offer_product['step_id'] ) : 0; $step_type = $step_id ? wcf()->utils->get_step_type( $step_id ) : ''; if ( 'upsell' === $step_type ) { $this->dispatch_offer_event( $order, $offer_product, 'upsell_accepted' ); } elseif ( 'downsell' === $step_type ) { $this->dispatch_offer_event( $order, $offer_product, 'downsell_accepted' ); } } /** * Handle offer rejected (upsell or downsell). * * Fired by cartflows_offer_rejected action. * * @param WC_Order $order Order object. * @param array $offer_product Offer product data. * @return void */ public function handle_offer_rejected( $order, $offer_product = array() ) { if ( ! $order instanceof WC_Order ) { return; } $step_id = isset( $offer_product['step_id'] ) ? absint( $offer_product['step_id'] ) : 0; $step_type = $step_id ? wcf()->utils->get_step_type( $step_id ) : ''; if ( 'upsell' === $step_type ) { $this->dispatch_offer_event( $order, $offer_product, 'upsell_skipped' ); } elseif ( 'downsell' === $step_type ) { $this->dispatch_offer_event( $order, $offer_product, 'downsell_skipped' ); } } /** * Dispatch an offer event webhook (upsell/downsell). * * @param WC_Order $order Order object. * @param array $offer_product Offer product data. * @param string $event Event name. * @return void */ private function dispatch_offer_event( $order, $offer_product, $event ) { $flow_id = $order->get_meta( '_wcf_flow_id' ); if ( empty( $flow_id ) || ! is_numeric( $flow_id ) ) { return; } $step_id = isset( $offer_product['step_id'] ) ? absint( $offer_product['step_id'] ) : 0; $product_id = isset( $offer_product['id'] ) ? absint( $offer_product['id'] ) : 0; $context = array( 'funnel_id' => intval( $flow_id ), 'step_id' => $step_id, 'order_id' => $order->get_id(), 'order' => $order, 'offer_product' => array( 'id' => $product_id, 'name' => isset( $offer_product['name'] ) ? $offer_product['name'] : '', ), ); wcf()->logger->log( 'Webhook Pro event: ' . $event . ' - Order ' . $order->get_id() ); Cartflows_Webhook_Dispatcher::get_instance()->dispatch( $event, $context ); } } }