product_id; } } } if ( ! class_exists( 'TMDO_Test_WC_Order' ) ) { final class TMDO_Test_WC_Order { /** * @param array $meta */ public function __construct( private array $meta, private float $total, private float $tax, private float $refunded, private int $product_id, private string $status = 'processing' ) {} public function get_status(): string { return $this->status; } public function get_meta( string $key ) { return $this->meta[ $key ] ?? ''; } public function get_total(): float { return $this->total; } public function get_total_tax(): float { return $this->tax; } public function get_total_refunded(): float { return $this->refunded; } public function get_items(): array { return array( new TMDO_Test_WC_Order_Item( $this->product_id ) ); } } } if ( ! function_exists( 'wc_get_order' ) ) { function wc_get_order( $order_id ) { return $GLOBALS['_tmdo_test_orders'][ (int) $order_id ] ?? false; } } if ( ! function_exists( 'get_post_field' ) ) { function get_post_field( string $field, $post ) { return $GLOBALS['_tmdo_test_post_fields'][ (int) $post ][ $field ] ?? ''; } } /** * Integration test: commission rows follow hivepress-marketplace's real order meta. * * Marketplace writes `hp_vendor` (vendor post ID), `hp_commission_rate` (vendor * share, e.g. 0.9 for a 10% commission) and `hp_commission_fee` (flat fee), and * pays vendors net_total * rate - fee (HivePress\Components\Marketplace::get_order_profit()). */ class WCOrdersInterceptorTest extends TestCase { private const ORDER_ID = 9001; private const VENDOR_POST = 7; private const VENDOR_USER = 42; private const PRODUCT_ID = 900; private const LISTING_ID = 555; public static function setUpBeforeClass(): void { TMDO_Installer::install_v2_tables(); } protected function setUp(): void { global $wpdb; $wpdb->prefix = 'wp_itest_'; $wpdb->options = 'wp_itest_options'; $wpdb->query( 'DELETE FROM `wp_itest_wpdo_wc_commissions` WHERE wc_order_id = ' . self::ORDER_ID ); delete_option( 'hp_vendor_include_taxes' ); TMDO_Feature_Flags::set( 'wc_orders', 'dual_write' ); $GLOBALS['_tmdo_test_post_fields'] = array( self::VENDOR_POST => array( 'post_author' => self::VENDOR_USER ), self::PRODUCT_ID => array( 'post_parent' => self::LISTING_ID ), ); } protected function tearDown(): void { TMDO_Feature_Flags::set( 'wc_orders', 'idle' ); unset( $GLOBALS['_tmdo_test_orders'], $GLOBALS['_tmdo_test_post_fields'] ); } private function place_order( array $meta, float $total, float $tax = 0.0, float $refunded = 0.0 ): void { $GLOBALS['_tmdo_test_orders'][ self::ORDER_ID ] = new TMDO_Test_WC_Order( $meta, $total, $tax, $refunded, self::PRODUCT_ID ); ( new TMDO_WC_Orders_Interceptor() )->action_order_status_changed( self::ORDER_ID, 'pending', 'processing' ); } /** The same order after a refund (and, for a full refund, a new status). */ private function refund( float $refunded, string $status ): void { $GLOBALS['_tmdo_test_orders'][ self::ORDER_ID ] = new TMDO_Test_WC_Order( $this->marketplace_meta(), 110.0, 10.0, $refunded, self::PRODUCT_ID, $status ); } private function commission_row(): ?array { global $wpdb; return $wpdb->get_row( 'SELECT * FROM `wp_itest_wpdo_wc_commissions` WHERE wc_order_id = ' . self::ORDER_ID, ARRAY_A ); } private function marketplace_meta(): array { return array( 'hp_vendor' => self::VENDOR_POST, 'hp_commission_rate' => 0.9, 'hp_commission_fee' => 5, ); } public function test_records_commission_from_marketplace_meta(): void { $this->place_order( $this->marketplace_meta(), 110.0, 10.0 ); $row = $this->commission_row(); $this->assertNotNull( $row ); $this->assertSame( self::VENDOR_USER, (int) $row['vendor_id'] ); $this->assertSame( self::LISTING_ID, (int) $row['listing_id'] ); $this->assertEqualsWithDelta( 100.0, (float) $row['subtotal'], 0.0001 ); $this->assertEqualsWithDelta( 85.0, (float) $row['vendor_payout'], 0.0001 ); $this->assertEqualsWithDelta( 15.0, (float) $row['commission'], 0.0001 ); $this->assertEqualsWithDelta( 10.0, (float) $row['commission_rate'], 0.0001 ); } public function test_includes_taxes_when_marketplace_setting_is_on(): void { update_option( 'hp_vendor_include_taxes', '1' ); $this->place_order( $this->marketplace_meta(), 110.0, 10.0 ); $row = $this->commission_row(); $this->assertEqualsWithDelta( 110.0, (float) $row['subtotal'], 0.0001 ); $this->assertEqualsWithDelta( 94.0, (float) $row['vendor_payout'], 0.0001 ); $this->assertEqualsWithDelta( 16.0, (float) $row['commission'], 0.0001 ); } public function test_skips_orders_without_marketplace_vendor(): void { $this->place_order( array(), 110.0 ); $this->assertNull( $this->commission_row() ); } public function test_vendor_payout_never_goes_negative(): void { $this->place_order( $this->marketplace_meta(), 3.0 ); $row = $this->commission_row(); $this->assertEqualsWithDelta( 0.0, (float) $row['vendor_payout'], 0.0001 ); $this->assertEqualsWithDelta( 3.0, (float) $row['commission'], 0.0001 ); } public function test_partial_refund_then_completion_records_the_refunded_amounts(): void { $this->place_order( $this->marketplace_meta(), 110.0, 10.0 ); $this->refund( 50.0, 'processing' ); $interceptor = new TMDO_WC_Orders_Interceptor(); $interceptor->action_order_status_changed( self::ORDER_ID, 'processing', 'completed' ); $interceptor->action_order_status_changed( self::ORDER_ID, 'processing', 'completed' ); // Duplicate event. global $wpdb; $this->assertSame( '1', (string) $wpdb->get_var( 'SELECT COUNT(*) FROM `wp_itest_wpdo_wc_commissions` WHERE wc_order_id = ' . self::ORDER_ID ) ); $row = $this->commission_row(); $this->assertSame( 'completed', $row['status'] ); $this->assertEqualsWithDelta( 50.0, (float) $row['subtotal'], 0.0001, '110 - 50 refunded - 10 tax' ); $this->assertEqualsWithDelta( 40.0, (float) $row['vendor_payout'], 0.0001 ); $this->assertEqualsWithDelta( 10.0, (float) $row['commission'], 0.0001 ); } public function test_a_refund_recomputes_the_commission_without_a_status_change(): void { $this->place_order( $this->marketplace_meta(), 110.0, 10.0 ); $this->refund( 50.0, 'processing' ); ( new TMDO_WC_Orders_Interceptor() )->action_order_refunded( self::ORDER_ID, 1 ); $this->assertEqualsWithDelta( 50.0, (float) $this->commission_row()['subtotal'], 0.0001 ); } public function test_a_full_refund_zeroes_a_recorded_commission(): void { $this->place_order( $this->marketplace_meta(), 110.0, 10.0 ); $this->refund( 110.0, 'refunded' ); ( new TMDO_WC_Orders_Interceptor() )->action_order_refunded( self::ORDER_ID, 1 ); $row = $this->commission_row(); $this->assertSame( 'refunded', $row['status'] ); $this->assertEqualsWithDelta( 0.0, (float) $row['vendor_payout'], 0.0001 ); } public function test_a_refund_of_an_untracked_order_records_nothing(): void { $this->refund( 50.0, 'pending' ); ( new TMDO_WC_Orders_Interceptor() )->action_order_refunded( self::ORDER_ID, 1 ); $this->assertNull( $this->commission_row() ); } public function test_refunds_are_hooked(): void { $interceptor = new TMDO_WC_Orders_Interceptor(); $interceptor->register_hooks(); $this->assertContains( array( $interceptor, 'action_order_refunded' ), $GLOBALS['_wp_filter_callbacks']['woocommerce_order_refunded'] ?? array() ); } }