diff --git a/woocommerce/Helpers/OrderHelper.php b/woocommerce/Helpers/OrderHelper.php new file mode 100644 index 000000000..52f7a36a8 --- /dev/null +++ b/woocommerce/Helpers/OrderHelper.php @@ -0,0 +1,145 @@ +|null + */ + private static ?\WeakMap $map = null; + + /** + * Sets a property on the order object. + * + * Stores a dynamic property either using WeakMap (PHP 8.0+) or direct property + * access (PHP 7.4+). The storage method is automatically determined based on + * PHP version and WeakMap availability. + * + * @since x.x.x + * + * @param \WC_Order $order The order object to store data on. + * @param string $key The property key. + * @param mixed $value The value to store. + * @return void + * + * @example + * ```php + * Dynamic_Props::set($order, 'customer_id', 123); + * Dynamic_Props::set($order, 'payment_total', '99.99'); + * ``` + */ + public static function set( \WC_Order &$order, string $key, mixed $value ): void { + if ( self::use_weak_map() ) { + self::init_weak_map(); + if ( ! isset( self::$map[ $order ] ) ) { + self::$map[ $order ] = new \stdClass(); + } + self::$map[ $order ]->{ $key } = $value; + } else { + $order->{ $key } = $value; + } + } + + /** + * Gets a property from the order object. + * + * Retrieves a stored dynamic property using the appropriate storage method + * based on PHP version. Supports nested property access. + * + * @since x.x.x + * + * @param \WC_Order $order The order object to retrieve data from. + * @param string $key The property key. + * @param string $nested_key Optional. The nested property key. Default null. + * @param mixed $default Optional. Default value if not found. Default null. + * @return mixed The stored value or default if not found. + * + * @example + * ```php + * $customer_id = Dynamic_Props::get($order, 'customer_id'); + * $total = Dynamic_Props::get($order, 'payment_total'); + * $token = Dynamic_Props::get($order, 'payment', 'token', 'DEFAULT_TOKEN'); + * ``` + */ + public static function get( \WC_Order $order, string $key, $nested_key = null, $default = null ): mixed { + if ( self::use_weak_map() ) { + self::init_weak_map(); + if ( is_null( $nested_key ) ) { + return self::$map[ $order ]->{ $key } ?? $default; + } else { + return self::$map[ $order ]->{ $key }->{ $nested_key } ?? $default; + } + } + if ( is_null( $nested_key ) ) { + return $order->{ $key } ?? $default; + } else { + return $order->{ $key }->{ $nested_key } ?? $default; + } + } + + /** + * Unsets a property on the order object. + * + * Removes a stored dynamic property using the appropriate storage method + * based on PHP version. + * + * @since x.x.x + * + * @param \WC_Order $order The order object to unset data from. + * @param string $key The property key to remove. + * @return void + */ + public static function unset( \WC_Order &$order, string $key ): void { + if ( self::use_weak_map() ) { + self::init_weak_map(); + unset( self::$map[ $order ]->{ $key } ); + } else { + unset( $order->{ $key } ); + } + } + + /** + * Checks if Dynamic_Props class should be used based on the filter. + * + * @return bool True if Dynamic_Props class should be used, false otherwise. + */ + private static function use_dynamic_props_class(): bool { + static $use_dynamic_props_class = null; + if ( null === $use_dynamic_props_class ) { + /** + * Filters whether to use Dynamic_Props class for storing order data. + * + * @since x.x.x + * + * @var bool Whether to Dynamic_Props class for storing order data. + */ + $use_dynamic_props_class = apply_filters( 'sv_wc_plugin_framework_use_dynamic_props_class', false ); + } + return $use_dynamic_props_class; + } + + /** + * Checks if WeakMap should be used based on PHP version. + * + * Determines whether to use WeakMap storage based on PHP version (8.0+) + * and WeakMap class availability. Result is cached for performance. + * + * @since x.x.x + * @return bool True if WeakMap should be used, false otherwise. + */ + private static function use_weak_map(): bool { + static $use_weak_map = null; + + if ( null === $use_weak_map ) { + $use_weak_map = version_compare( PHP_VERSION, '8.0', '>=' ) && self::use_dynamic_props_class(); + } + + return $use_weak_map; + } + + /** + * Initializes WeakMap storage if not already initialized. + * + * Ensures the WeakMap storage is initialized only once when needed. + * This lazy initialization helps with performance and memory usage. + * + * @since x.x.x + * @return void + */ + private static function init_weak_map(): void { + if ( null === self::$map ) { + self::$map = new \WeakMap(); + } + } +} diff --git a/woocommerce/payment-gateway/Handlers/Abstract_Hosted_Payment_Handler.php b/woocommerce/payment-gateway/Handlers/Abstract_Hosted_Payment_Handler.php index 84c9f483f..216388310 100644 --- a/woocommerce/payment-gateway/Handlers/Abstract_Hosted_Payment_Handler.php +++ b/woocommerce/payment-gateway/Handlers/Abstract_Hosted_Payment_Handler.php @@ -25,6 +25,7 @@ namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Handlers; use SkyVerge\WooCommerce\PluginFramework\v6_0_0 as FrameworkBase; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers\OrderHelper; if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v6_0_0\\Payment_Gateway\\Handlers\\Abstract_Hosted_Payment_Handler' ) ) : @@ -325,20 +326,25 @@ protected function get_order_from_response( FrameworkBase\SV_WC_Payment_Gateway_ $order = $this->get_gateway()->get_order( $order ); - $order->payment->account_number = $response->get_account_number(); + $payment = OrderHelper::get_payment( $order ); + + $payment->account_number = $response->get_account_number(); if ( $response instanceof FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_Credit_Card_Response ) { - $order->payment->exp_month = $response->get_exp_month(); - $order->payment->exp_year = $response->get_exp_year(); - $order->payment->card_type = $response->get_card_type(); + $payment->exp_month = $response->get_exp_month(); + $payment->exp_year = $response->get_exp_year(); + $payment->card_type = $response->get_card_type(); } elseif ( $response instanceof FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_eCheck_Response ) { - $order->payment->account_type = $response->get_account_type(); - $order->payment->check_number = $response->get_check_number(); + $payment->account_type = $response->get_account_type(); + $payment->check_number = $response->get_check_number(); } + // Set payment info on the order object. + OrderHelper::set_payment( $order, $payment ); + return $order; } diff --git a/woocommerce/payment-gateway/Handlers/Capture.php b/woocommerce/payment-gateway/Handlers/Capture.php index d5cadbb6c..3a125eaa1 100644 --- a/woocommerce/payment-gateway/Handlers/Capture.php +++ b/woocommerce/payment-gateway/Handlers/Capture.php @@ -25,6 +25,7 @@ namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Handlers; use SkyVerge\WooCommerce\PluginFramework\v6_0_0 as Framework; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Dynamic_Props; defined( 'ABSPATH' ) or exit; @@ -189,7 +190,7 @@ public function perform_capture( \WC_Order $order, $amount = null ) { /* translators: Placeholders: %1$s - payment gateway title (such as Authorize.net, Braintree, etc), %2$s - transaction amount. Definitions: Capture, as in capture funds from a credit card. */ __( '%1$s Capture of %2$s Approved', 'woocommerce-plugin-framework' ), $this->get_gateway()->get_method_title(), - wc_price( $order->capture->amount, [ + wc_price( Dynamic_Props::get( $order, 'capture', 'amount' ), [ 'currency' => $order->get_currency() ] ) ); @@ -255,7 +256,7 @@ public function perform_capture( \WC_Order $order, $amount = null ) { */ public function do_capture_success( \WC_Order $order, Framework\SV_WC_Payment_Gateway_API_Response $response ) { - $total_captured = (float) $this->get_gateway()->get_order_meta( $order, 'capture_total' ) + (float) $order->capture->amount; + $total_captured = (float) $this->get_gateway()->get_order_meta( $order, 'capture_total' ) + (float) Dynamic_Props::get( $order, 'capture', 'amount' ); $this->get_gateway()->update_order_meta( $order, 'capture_total', Framework\SV_WC_Helper::number_format( $total_captured ) ); $this->get_gateway()->update_order_meta( $order, 'charge_captured', $this->get_gateway()->supports_credit_card_partial_capture() && $this->get_gateway()->is_partial_capture_enabled() && $total_captured < (float) $this->get_order_capture_maximum( $order ) ? 'partial' : 'yes' ); diff --git a/woocommerce/payment-gateway/class-sv-wc-payment-gateway-direct.php b/woocommerce/payment-gateway/class-sv-wc-payment-gateway-direct.php index a03f81780..8cb03e3ca 100644 --- a/woocommerce/payment-gateway/class-sv-wc-payment-gateway-direct.php +++ b/woocommerce/payment-gateway/class-sv-wc-payment-gateway-direct.php @@ -25,6 +25,8 @@ namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0; use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Blocks\Blocks_Handler; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers\OrderHelper; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Dynamic_Props; defined( 'ABSPATH' ) or exit; @@ -372,7 +374,7 @@ public function process_payment( $order_id ) { if ( $this->supports_tokenization() && 0 !== (int) $order->get_user_id() ) { // if already paying with an existing method, try and updated it locally and remotely - if ( ! empty( $order->payment->token ) ) { + if ( ! empty( Dynamic_Props::get( $order, 'payment', 'token' ) ) ) { $this->update_transaction_payment_method( $order ); @@ -388,7 +390,7 @@ public function process_payment( $order_id ) { if ( $this->should_skip_transaction( $order ) || $this->do_transaction( $order ) ) { // add transaction data for zero-dollar "orders" - if ( '0.00' === $order->payment_total ) { + if ( '0.00' === OrderHelper::get_payment_total( $order ) ) { $this->add_transaction_data( $order ); } @@ -502,7 +504,7 @@ protected function get_notices_as_user_messages( ?string $type = null ) : array */ protected function update_transaction_payment_method( \WC_Order $order ) { - $token = $this->get_payment_tokens_handler()->get_token( $order->get_user_id(), $order->payment->token ); + $token = $this->get_payment_tokens_handler()->get_token( $order->get_user_id(), Dynamic_Props::get( $order, 'payment', 'token' ) ); $address = new Addresses\Customer_Address(); $address->set_from_order( $order ); @@ -566,8 +568,11 @@ protected function update_transaction_payment_method( \WC_Order $order ) { /** - * Add payment and transaction information as class members of WC_Order - * instance. The standard information that can be added includes: + * Add payment and transaction information to $order object. + * + * The standard information that can be added includes: + * + * It is set and get using Dynamic_Props class and not directly on $order object, but documented here for reference. * * $order->payment_total - the payment total * $order->customer_id - optional payment gateway customer id (useful for tokenized payments for certain gateways, etc) @@ -599,43 +604,46 @@ public function get_order( $order_id ) { $order = parent::get_order( $order_id ); + // Get payment info from the order object. + $payment = OrderHelper::get_payment( $order ); + // payment info if ( SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-account-number' ) && ! SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-payment-token' ) ) { // common attributes - $order->payment->account_number = str_replace( array( ' ', '-' ), '', SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-account-number' ) ); - $order->payment->last_four = substr( $order->payment->account_number, -4 ); + $payment->account_number = str_replace( array( ' ', '-' ), '', SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-account-number' ) ); + $payment->last_four = substr( $payment->account_number, -4 ); if ( $this->is_credit_card_gateway() ) { // credit card specific attributes - $order->payment->card_type = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-card-type' ); - $order->payment->exp_month = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-exp-month' ); - $order->payment->exp_year = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-exp-year' ); + $payment->card_type = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-card-type' ); + $payment->exp_month = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-exp-month' ); + $payment->exp_year = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-exp-year' ); // add card type for gateways that don't require it displayed at checkout - if ( empty( $order->payment->card_type ) ) { - $order->payment->card_type = SV_WC_Payment_Gateway_Helper::card_type_from_account_number( $order->payment->account_number ); + if ( empty( $payment->card_type ) ) { + $payment->card_type = SV_WC_Payment_Gateway_Helper::card_type_from_account_number( $payment->account_number ); } // handle single expiry field formatted like "MM / YY" or "MM / YYYY" if ( SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-expiry' ) ) { - [ $order->payment->exp_month, $order->payment->exp_year ] = array_map( 'trim', explode( '/', SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-expiry' ) ) ); + [ $payment->exp_month, $payment->exp_year ] = array_map( 'trim', explode( '/', SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-expiry' ) ) ); } // add CSC if enabled if ( $this->csc_enabled() ) { - $order->payment->csc = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-csc' ); + $payment->csc = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-csc' ); } } elseif ( $this->is_echeck_gateway() ) { // echeck specific attributes - $order->payment->routing_number = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-routing-number' ); - $order->payment->account_type = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-account-type' ); - $order->payment->check_number = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-check-number' ); - $order->payment->drivers_license_number = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-drivers-license-number' ); - $order->payment->drivers_license_state = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-drivers-license-state' ); + $payment->routing_number = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-routing-number' ); + $payment->account_type = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-account-type' ); + $payment->check_number = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-check-number' ); + $payment->drivers_license_number = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-drivers-license-number' ); + $payment->drivers_license_state = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-drivers-license-state' ); } @@ -644,25 +652,25 @@ public function get_order( $order_id ) { // paying with tokenized payment method (we've already verified that this token exists in the validate_fields method) $token = $this->get_payment_tokens_handler()->get_token( $order->get_user_id(), $token_value ); - $order->payment->token = $token->get_id(); - $order->payment->account_number = $token->get_last_four(); - $order->payment->last_four = $token->get_last_four(); + $payment->token = $token->get_id(); + $payment->account_number = $token->get_last_four(); + $payment->last_four = $token->get_last_four(); if ( $this->is_credit_card_gateway() ) { // credit card specific attributes - $order->payment->card_type = $token->get_card_type(); - $order->payment->exp_month = $token->get_exp_month(); - $order->payment->exp_year = $token->get_exp_year(); + $payment->card_type = $token->get_card_type(); + $payment->exp_month = $token->get_exp_month(); + $payment->exp_year = $token->get_exp_year(); if ( $this->csc_enabled_for_tokens() ) { - $order->payment->csc = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-csc' ); + $payment->csc = SV_WC_Helper::get_posted_value( 'wc-' . $this->get_id_dasherized() . '-csc' ); } } elseif ( $this->is_echeck_gateway() ) { // echeck specific attributes - $order->payment->account_type = $token->get_account_type(); + $payment->account_type = $token->get_account_type(); } // make this the new default payment token @@ -670,10 +678,13 @@ public function get_order( $order_id ) { } // standardize expiration date year to 2 digits - if ( ! empty( $order->payment->exp_year ) && 4 === strlen( $order->payment->exp_year ) ) { - $order->payment->exp_year = substr( $order->payment->exp_year, 2 ); + if ( ! empty( $payment->exp_year ) && 4 === strlen( $payment->exp_year ) ) { + $payment->exp_year = substr( $payment->exp_year, 2 ); } + // Set payment info on the order object. + OrderHelper::set_payment( $order, $payment ); + /** * Direct Gateway Get Order Filter. * @@ -706,16 +717,16 @@ protected function do_check_transaction( $order, $response = null ) { // success! update order record if ( $response->transaction_approved() ) { - $last_four = substr( $order->payment->account_number, -4 ); + $last_four = substr( Dynamic_Props::get( $order, 'payment', 'account_number', '' ), -4 ); // check order note. there may not be an account_type available, but that's fine /* translators: Placeholders: %1$s - Payment method title, %2$s - Payment account type (savings/checking) (may or may not be available), %3$s - Last four digits of the account */ - $message = sprintf( esc_html__( '%1$s Check Transaction Approved: %2$s account ending in %3$s', 'woocommerce-plugin-framework' ), $this->get_method_title(), $order->payment->account_type, $last_four ); + $message = sprintf( esc_html__( '%1$s Check Transaction Approved: %2$s account ending in %3$s', 'woocommerce-plugin-framework' ), $this->get_method_title(), Dynamic_Props::get( $order, 'payment', 'account_type' ), $last_four ); // optional check number - if ( ! empty( $order->payment->check_number ) ) { + if ( ! empty( Dynamic_Props::get( $order, 'payment', 'check_number' ) ) ) { /* translators: Placeholder: %s - Bank check number */ - $message .= '. ' . sprintf( esc_html__( 'Check number %s', 'woocommerce-plugin-framework' ), $order->payment->check_number ); + $message .= '. ' . sprintf( esc_html__( 'Check number %s', 'woocommerce-plugin-framework' ), Dynamic_Props::get( $order, 'payment', 'check_number' ) ); } // adds the transaction id (if any) to the order note @@ -771,12 +782,12 @@ protected function do_credit_card_transaction( $order, $response = null ) { // success! update order record if ( $response->transaction_approved() ) { - $account_number = ! empty( $order->payment->account_number ) ? $order->payment->account_number : null; + $account_number = ! empty( Dynamic_Props::get( $order, 'payment', 'account_number' ) ) ? Dynamic_Props::get( $order, 'payment', 'account_number' ) : null; $last_four = $account_number ? substr( $account_number, -4 ) : ''; // use direct card type if set, or try to guess it from card number - if ( ! empty( $order->payment->card_type ) ) { - $card_type = $order->payment->card_type; + if ( ! empty( Dynamic_Props::get( $order, 'payment', 'card_type' ) ) ) { + $card_type = Dynamic_Props::get( $order, 'payment', 'card_type' ); } elseif ( $first_four = $account_number ? substr( $account_number, 0, 4 ) : '' ) { $card_type = SV_WC_Payment_Gateway_Helper::card_type_from_account_number( $first_four ); } else { @@ -806,12 +817,12 @@ protected function do_credit_card_transaction( $order, $response = null ) { } // add the expiry date if it is available - if ( ! empty( $order->payment->exp_month ) && ! empty( $order->payment->exp_year ) ) { + if ( ! empty( Dynamic_Props::get( $order, 'payment', 'exp_month' ) ) && ! empty( Dynamic_Props::get( $order, 'payment', 'exp_year' ) ) ) { $message .= ' ' . sprintf( /* translators: Placeholder: %s - Credit card expiry date */ __( '(expires %s)', 'woocommerce-plugin-framework' ), - $order->payment->exp_month . '/' . substr( $order->payment->exp_year, -2 ) + Dynamic_Props::get( $order, 'payment', 'exp_month', '' ) . '/' . substr( Dynamic_Props::get( $order, 'payment', 'exp_year', '' ), -2 ) ); } @@ -1131,13 +1142,14 @@ protected function get_order_for_add_payment_method() { $order->set_props( $properties ); // other default info - $order->customer_id = $this->get_customer_id( $order->get_user_id() ); + OrderHelper::set_customer_id( $order, $this->get_customer_id( $order->get_user_id() ) ); /* translators: Placeholders: %1$s - site title, %2$s - customer email. Payment method as in a specific credit card, e-check or bank account */ - $order->description = sprintf( esc_html__( '%1$s - Add Payment Method for %2$s', 'woocommerce-plugin-framework' ), sanitize_text_field( SV_WC_Helper::get_site_name() ), $properties['billing_email'] ); + $description = sprintf( esc_html__( '%1$s - Add Payment Method for %2$s', 'woocommerce-plugin-framework' ), sanitize_text_field( SV_WC_Helper::get_site_name() ), $properties['billing_email'] ); + Dynamic_Props::set( $order, 'description', $description ); // force zero amount - $order->payment_total = '0.00'; + OrderHelper::set_payment_total( $order, '0.00' ); /** * Direct Gateway Get Order for Add Payment Method Filter. @@ -1168,12 +1180,12 @@ protected function add_add_payment_method_customer_data( $order, $response ) { // set customer ID from response if available if ( $this->supports_customer_id() && method_exists( $response, 'get_customer_id' ) && $response->get_customer_id() ) { - $order->customer_id = $customer_id = $response->get_customer_id(); + OrderHelper::set_customer_id( $order, $customer_id = $response->get_customer_id() ); } else { // default to the customer ID on "order" - $customer_id = $order->customer_id; + $customer_id = OrderHelper::get_customer_id( $order ); } // update the user @@ -1283,7 +1295,7 @@ public function transaction_forced() { */ protected function should_tokenize_before_sale( \WC_Order $order ): bool { - $result = $this->get_payment_tokens_handler()->should_tokenize() && ( '0.00' === $order->payment_total || $this->tokenize_before_sale() ); + $result = $this->get_payment_tokens_handler()->should_tokenize() && ( '0.00' === OrderHelper::get_payment_total( $order ) || $this->tokenize_before_sale() ); /** * Filters whether tokenization should be performed before the sale, for a given order. @@ -1374,7 +1386,7 @@ protected function can_tokenize_with_or_after_sale( \WC_Order $order ): bool { */ return apply_filters( "wc_payment_gateway_{$this->get_id()}_can_tokenize_with_or_after_sale", - $order->payment_total > 0, + OrderHelper::get_payment_total( $order ) > 0, $order, $this ); @@ -1411,7 +1423,7 @@ protected function should_skip_transaction( \WC_Order $order ): bool { return apply_filters( "wc_payment_gateway_{$this->get_id()}_should_skip_transaction", // the order amount will be $0 if a WooCommerce Subscriptions free trial product is being processed - ( '0.00' === $order->payment_total && ! $this->transaction_forced() ), + ( '0.00' === OrderHelper::get_payment_total( $order ) && ! $this->transaction_forced() ), $order, $this ); diff --git a/woocommerce/payment-gateway/class-sv-wc-payment-gateway-hosted.php b/woocommerce/payment-gateway/class-sv-wc-payment-gateway-hosted.php index 51cce831c..e37a2496b 100644 --- a/woocommerce/payment-gateway/class-sv-wc-payment-gateway-hosted.php +++ b/woocommerce/payment-gateway/class-sv-wc-payment-gateway-hosted.php @@ -24,6 +24,9 @@ namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers\OrderHelper; + + defined( 'ABSPATH' ) or exit; if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v6_0_0\\SV_WC_Payment_Gateway_Hosted' ) ) : @@ -465,20 +468,25 @@ protected function get_order_from_response( $response ) { $order = $this->get_order( $order ); - $order->payment->account_number = $response->get_account_number(); + $payment = OrderHelper::get_payment( $order ); + + $payment->account_number = $response->get_account_number(); if ( self::PAYMENT_TYPE_CREDIT_CARD == $response->get_payment_type() ) { - $order->payment->exp_month = $response->get_exp_month(); - $order->payment->exp_year = $response->get_exp_year(); - $order->payment->card_type = $response->get_card_type(); + $payment->exp_month = $response->get_exp_month(); + $payment->exp_year = $response->get_exp_year(); + $payment->card_type = $response->get_card_type(); } elseif ( self::PAYMENT_TYPE_ECHECK == $response->get_payment_type() ) { - $order->payment->account_type = $response->get_account_type(); - $order->payment->check_number = $response->get_check_number(); + $payment->account_type = $response->get_account_type(); + $payment->check_number = $response->get_check_number(); } + // Set payment info on the order object. + OrderHelper::set_payment( $order, $payment ); + return $order; } @@ -596,7 +604,7 @@ protected function process_transaction_response( $order, $response ) { protected function process_tokenization_response( \WC_Order $order, $response ) { if ( is_callable( array( $response, 'get_customer_id' ) ) && $response->get_customer_id() ) { - $order->customer_id = $response->get_customer_id(); + OrderHelper::set_customer_id( $order, $response->get_customer_id() ); } $token = $response->get_payment_token(); @@ -655,21 +663,26 @@ protected function process_tokenization_response( \WC_Order $order, $response ) // add the payment method order data if ( $token ) { - $order->payment->token = $token->get_id(); - $order->payment->account_number = $token->get_last_four(); - $order->payment->last_four = $token->get_last_four(); + $payment = OrderHelper::get_payment( $order ); + + $payment->token = $token->get_id(); + $payment->account_number = $token->get_last_four(); + $payment->last_four = $token->get_last_four(); if ( $token->is_credit_card() ) { - $order->payment->exp_month = $token->get_exp_month(); - $order->payment->exp_year = $token->get_exp_year(); - $order->payment->card_type = $token->get_card_type(); + $payment->exp_month = $token->get_exp_month(); + $payment->exp_year = $token->get_exp_year(); + $payment->card_type = $token->get_card_type(); } elseif ( $token->is_echeck() ) { - $order->payment->account_type = $token->get_account_type(); - $order->payment->check_number = $token->get_check_number(); + $payment->account_type = $token->get_account_type(); + $payment->check_number = $token->get_check_number(); } + + // Set payment info on the order object + OrderHelper::set_payment( $order, $payment ); } // remove any tokens that were deleted on the hosted pay page diff --git a/woocommerce/payment-gateway/class-sv-wc-payment-gateway.php b/woocommerce/payment-gateway/class-sv-wc-payment-gateway.php index 2392e1df4..7b6942040 100755 --- a/woocommerce/payment-gateway/class-sv-wc-payment-gateway.php +++ b/woocommerce/payment-gateway/class-sv-wc-payment-gateway.php @@ -26,7 +26,9 @@ use Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface; use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Blocks\Blocks_Handler; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers\OrderHelper; use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Blocks\Gateway_Checkout_Block_Integration; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Dynamic_Props; use stdClass; defined( 'ABSPATH' ) or exit; @@ -1281,9 +1283,13 @@ public function get_apple_pay_currencies() { */ public function get_order_for_apple_pay( \WC_Order $order, SV_WC_Payment_Gateway_Apple_Pay_Payment_Response $response ) { - $order->payment->account_number = $response->get_last_four(); - $order->payment->last_four = $response->get_last_four(); - $order->payment->card_type = $response->get_card_type(); + $payment = OrderHelper::get_payment( $order ); + + $payment->account_number = $response->get_last_four(); + $payment->last_four = $response->get_last_four(); + $payment->card_type = $response->get_card_type(); + + OrderHelper::set_payment( $order, $payment ); return $order; } @@ -1336,11 +1342,16 @@ public function get_order_for_google_pay( \WC_Order $order, $response_data ) { $payment_method_data = $response_data['paymentMethodData']; - $order->payment->google_pay = base64_encode( $payment_method_data['tokenizationData']['token'] ); + $payment = OrderHelper::get_payment( $order ); + + $payment->google_pay = base64_encode( $payment_method_data['tokenizationData']['token'] ); // account last four - $order->payment->account_number = $payment_method_data['info']['cardDetails']; - $order->payment->card_type = SV_WC_Payment_Gateway_Helper::normalize_card_type( $payment_method_data['info']['cardNetwork'] ); + $payment->account_number = $payment_method_data['info']['cardDetails']; + $payment->card_type = SV_WC_Payment_Gateway_Helper::normalize_card_type( $payment_method_data['info']['cardNetwork'] ); + + // Set payment info on the order object. + OrderHelper::set_payment( $order, $payment ); return $order; } @@ -1906,10 +1917,12 @@ public function get_payment_method_image_url( $type ) { /** - * Adds payment and transaction information as class members of {@see WC_Order} instance. + * Adds payment and transaction information on $order object. * * The standard information that can be added includes: * + * It is set and get using Dynamic_Props class and not directly on $order object, but documented here for reference. + * * $order->payment_total - the payment total * $order->customer_id - optional payment gateway customer id (useful for tokenized payments, etc) * $order->payment->type - one of 'credit_card' or 'check' @@ -1933,23 +1946,30 @@ public function get_order( $order ) { } // set payment total here, so it can be modified for later by add-ons like subscriptions which may need to charge an amount different than the get_total() - $order->payment_total = number_format( $order->get_total(), 2, '.', '' ); + $payment_total = number_format( $order->get_total(), 2, '.', '' ); + + OrderHelper::set_payment_total( $order, $payment_total ); - $order->customer_id = ''; + OrderHelper::set_customer_id( $order, '' ); // logged in customer? if ( 0 != $order->get_user_id() && false !== ( $customer_id = $this->get_customer_id( $order->get_user_id(), array( 'order' => $order ) ) ) ) { - $order->customer_id = $customer_id; + OrderHelper::set_customer_id( $order, $customer_id ); } // add payment info - $order->payment = new \stdClass(); + $payment = new \stdClass(); // payment type (credit_card/check/etc) - $order->payment->type = str_replace( '-', '_', $this->get_payment_type() ); + $payment->type = str_replace( '-', '_', $this->get_payment_type() ); + + // Set payment info on the order object + OrderHelper::set_payment( $order, $payment ); /* translators: Placeholders: %1$s - site title, %2$s - order number */ - $order->description = sprintf( esc_html__( '%1$s - Order %2$s', 'woocommerce-plugin-framework' ), wp_specialchars_decode( SV_WC_Helper::get_site_name(), ENT_QUOTES ), $order->get_order_number() ); + $description = sprintf( esc_html__( '%1$s - Order %2$s', 'woocommerce-plugin-framework' ), wp_specialchars_decode( SV_WC_Helper::get_site_name(), ENT_QUOTES ), $order->get_order_number() ); + + Dynamic_Props::set( $order, 'description', $description ); // the get_order_with_unique_transaction_ref() call results in saving the order object, which we don't want to do if the order hasn't already been saved (such as when adding a payment method) if ( $order->get_id() ) { @@ -2069,7 +2089,7 @@ public function get_order_for_capture( $order, $amount = null ) { } // add capture info - $order->capture = new \stdClass(); + $capture = new \stdClass(); $total_captured = $this->get_order_meta( $order, 'capture_total' ); @@ -2078,11 +2098,14 @@ public function get_order_for_capture( $order, $amount = null ) { $amount = (float) $order->get_total() - (float) $total_captured; } - $order->capture->amount = SV_WC_Helper::number_format( $amount ); + $capture->amount = SV_WC_Helper::number_format( $amount ); /* translators: Placeholders: %1$s - site title, %2$s - order number. Definitions: Capture as in capture funds from a credit card. */ - $order->capture->description = sprintf( esc_html__( '%1$s - Capture for Order %2$s', 'woocommerce-plugin-framework' ), wp_specialchars_decode( SV_WC_Helper::get_site_name() ), $order->get_order_number() ); - $order->capture->trans_id = $this->get_order_meta( $order, 'trans_id' ); + $capture->description = sprintf( esc_html__( '%1$s - Capture for Order %2$s', 'woocommerce-plugin-framework' ), wp_specialchars_decode( SV_WC_Helper::get_site_name() ), $order->get_order_number() ); + $capture->trans_id = $this->get_order_meta( $order, 'trans_id' ); + + // Set capture info on the order object. + Dynamic_Props::set( $order, 'capture', $capture ); /** * Direct Gateway Capture Get Order Filter. @@ -2196,8 +2219,11 @@ public function process_refund( $order_id, $amount = null, $reason = '' ) { /** - * Add refund information as class members of WC_Order - * instance for use in refund transactions. Standard information includes: + * Add refund information to $order object. + * + * It is set and get using Dynamic_Props class and not directly on $order object, but documented here for reference. + * + * Standard information includes: * * $order->refund->amount = refund amount * $order->refund->reason = user-entered reason text for the refund @@ -2220,14 +2246,17 @@ protected function get_order_for_refund( $order, $amount, $reason ) { } // add refund info - $order->refund = new \stdClass(); - $order->refund->amount = number_format( $amount, 2, '.', '' ); + $refund = new \stdClass(); + $refund->amount = number_format( $amount, 2, '.', '' ); /* translators: Placeholders: %1$s - site title, %2$s - order number */ - $order->refund->reason = $reason ? $reason : sprintf( esc_html__( '%1$s - Refund for Order %2$s', 'woocommerce-plugin-framework' ), esc_html( SV_WC_Helper::get_site_name() ), $order->get_order_number() ); + $refund->reason = $reason ? $reason : sprintf( esc_html__( '%1$s - Refund for Order %2$s', 'woocommerce-plugin-framework' ), esc_html( SV_WC_Helper::get_site_name() ), $order->get_order_number() ); // almost all gateways require the original transaction ID, so include it by default - $order->refund->trans_id = $this->get_order_meta( $order, 'trans_id' ); + $refund->trans_id = $this->get_order_meta( $order, 'trans_id' ); + + // Set refund info on the order object. + Dynamic_Props::set( $order, 'refund', $refund ); /** * Payment Gateway Get Order For Refund Filter. @@ -2257,7 +2286,7 @@ protected function get_order_for_refund( $order, $amount, $reason ) { protected function add_refund_data( \WC_Order $order, $response ) { // indicate the order was refunded along with the refund amount - $this->add_order_meta( $order, 'refund_amount', $order->refund->amount ); + $this->add_order_meta( $order, 'refund_amount', Dynamic_Props::get( $order, 'refund', 'amount' ) ); // add refund transaction ID if ( $response && $response->get_transaction_id() ) { @@ -2293,7 +2322,7 @@ protected function add_refund_order_note( \WC_Order $order, $response ) { /* translators: Placeholders: %1$s - payment gateway title (such as Authorize.net, Braintree, etc), %2$s - a monetary amount */ esc_html__( '%1$s Refund in the amount of %2$s approved.', 'woocommerce-plugin-framework' ), $this->get_method_title(), - wc_price( $order->refund->amount, [ + wc_price( Dynamic_Props::get( $order, 'refund', 'amount' ), [ 'currency' => $order->get_currency() ] ) ); @@ -2404,7 +2433,7 @@ protected function maybe_void_instead_of_refund( $order, $response ) { protected function process_void( \WC_Order $order ) { // partial voids are not supported - if ( $order->refund->amount != $order->get_total() ) { + if ( Dynamic_Props::get( $order, 'refund', 'amount' ) != $order->get_total() ) { return new \WP_Error( 'wc_' . $this->get_id() . '_void_error', esc_html__( 'Oops, you cannot partially void this order. Please use the full order amount.', 'woocommerce-plugin-framework' ), 500 ); } @@ -2456,7 +2485,7 @@ protected function process_void( \WC_Order $order ) { protected function add_void_data( \WC_Order $order, $response ) { // indicate the order was voided along with the amount - $this->update_order_meta( $order, 'void_amount', $order->refund->amount ); + $this->update_order_meta( $order, 'void_amount', Dynamic_Props::get( $order, 'refund', 'amount' ) ); // add refund transaction ID if ( $response && $response->get_transaction_id() ) { @@ -2526,7 +2555,7 @@ public function mark_order_as_voided( $order, $response ) { /* translators: Placeholders: %1$s - payment gateway title, %2$s - a monetary amount. Void as in to void an order. */ esc_html__( '%1$s Void in the amount of %2$s approved.', 'woocommerce-plugin-framework' ), $this->get_method_title(), - wc_price( $order->refund->amount, [ + wc_price( Dynamic_Props::get( $order, 'refund', 'amount' ), [ 'currency' => $order->get_currency() ] ) ); @@ -2599,7 +2628,9 @@ protected function get_order_with_unique_transaction_ref( $order ) { $this->update_order_meta( $order, 'retry_count', $retry_count ); // generate a unique transaction ref based on the order number and retry count, for gateways that require a unique identifier for every transaction request - $order->unique_transaction_ref = ltrim( $order->get_order_number(), esc_html_x( '#', 'hash before order number', 'woocommerce-plugin-framework' ) ) . ( $retry_count > 0 ? '-' . $retry_count : '' ); + $unique_transaction_ref = ltrim( $order->get_order_number(), esc_html_x( '#', 'hash before order number', 'woocommerce-plugin-framework' ) ) . ( $retry_count > 0 ? '-' . $retry_count : '' ); + + Dynamic_Props::set( $order, 'unique_transaction_ref', $unique_transaction_ref ); return $order; } @@ -2676,13 +2707,15 @@ public function add_transaction_data( $order, $response = null ) { $this->add_customer_data( $order, $response ); } - if ( isset( $order->payment->token ) && $order->payment->token ) { - $this->update_order_meta( $order, 'payment_token', $order->payment->token ); + $payment = OrderHelper::get_payment( $order ); + + if ( isset( $payment->token ) && $payment->token ) { + $this->update_order_meta( $order, 'payment_token', $payment->token ); } // account number - if ( isset( $order->payment->account_number ) && $order->payment->account_number ) { - $this->update_order_meta( $order, 'account_four', substr( $order->payment->account_number, -4 ) ); + if ( isset( $payment->account_number ) && $payment->account_number ) { + $this->update_order_meta( $order, 'account_four', substr( $payment->account_number, -4 ) ); } if ( $this->is_credit_card_gateway() ) { @@ -2690,13 +2723,13 @@ public function add_transaction_data( $order, $response = null ) { // credit card gateway data if ( $response instanceof SV_WC_Payment_Gateway_API_Authorization_Response ) { - $this->update_order_meta( $order, 'authorization_amount', $order->payment_total ); + $this->update_order_meta( $order, 'authorization_amount', OrderHelper::get_payment_total( $order ) ); if ( $response->get_authorization_code() ) { $this->update_order_meta( $order, 'authorization_code', $response->get_authorization_code() ); } - if ( $order->payment_total > 0 ) { + if ( OrderHelper::get_payment_total( $order ) > 0 ) { // mark as captured if ( $this->perform_credit_card_charge( $order ) ) { @@ -2709,12 +2742,12 @@ public function add_transaction_data( $order, $response = null ) { } } - if ( isset( $order->payment->exp_year ) && $order->payment->exp_year && isset( $order->payment->exp_month ) && $order->payment->exp_month ) { - $this->update_order_meta( $order, 'card_expiry_date', $order->payment->exp_year . '-' . $order->payment->exp_month ); + if ( isset( $payment->exp_year ) && $payment->exp_year && isset( $payment->exp_month ) && $payment->exp_month ) { + $this->update_order_meta( $order, 'card_expiry_date', $payment->exp_year . '-' . $payment->exp_month ); } - if ( isset( $order->payment->card_type ) && $order->payment->card_type ) { - $this->update_order_meta( $order, 'card_type', $order->payment->card_type ); + if ( isset( $payment->card_type ) && $payment->card_type ) { + $this->update_order_meta( $order, 'card_type', $payment->card_type ); } } elseif ( $this->is_echeck_gateway() ) { @@ -2722,13 +2755,13 @@ public function add_transaction_data( $order, $response = null ) { // checking gateway data // optional account type (checking/savings) - if ( isset( $order->payment->account_type ) && $order->payment->account_type ) { - $this->update_order_meta( $order, 'account_type', $order->payment->account_type ); + if ( isset( $payment->account_type ) && $payment->account_type ) { + $this->update_order_meta( $order, 'account_type', $payment->account_type ); } // optional check number - if ( isset( $order->payment->check_number ) && $order->payment->check_number ) { - $this->update_order_meta( $order, 'check_number', $order->payment->check_number ); + if ( isset( $payment->check_number ) && $payment->check_number ) { + $this->update_order_meta( $order, 'check_number', $payment->check_number ); } } @@ -2779,12 +2812,13 @@ protected function add_customer_data( $order, $response = null ) { if ( $response && method_exists( $response, 'get_customer_id' ) && $response->get_customer_id() ) { - $order->customer_id = $customer_id = $response->get_customer_id(); + $customer_id = $response->get_customer_id(); + OrderHelper::set_customer_id( $order, $customer_id ); } else { // default to the customer ID set on the order - $customer_id = $order->customer_id; + $customer_id = OrderHelper::get_customer_id( $order ); } // update the order with the customer ID, note environment is not appended here because it's already available @@ -2810,12 +2844,14 @@ protected function add_customer_data( $order, $response = null ) { */ public function get_credit_card_transaction_approved_message( \WC_Order $order, $response ) { - $last_four = ! empty( $order->payment->last_four ) ? $order->payment->last_four : substr( $order->payment->account_number, -4 ); + $payment = OrderHelper::get_payment( $order ); + + $last_four = ! empty( $payment->last_four ) ? $payment->last_four : substr( $payment->account_number, -4 ); // use direct card type if set, or try to guess it from card number - if ( ! empty( $order->payment->card_type ) ) { - $card_type = $order->payment->card_type; - } elseif ( $first_four = substr( $order->payment->account_number, 0, 4 ) ) { + if ( ! empty( $payment->card_type ) ) { + $card_type = $payment->card_type; + } elseif ( $first_four = substr( $payment->account_number, 0, 4 ) ) { $card_type = SV_WC_Payment_Gateway_Helper::card_type_from_account_number( $first_four ); } else { $card_type = 'card'; @@ -2840,12 +2876,12 @@ public function get_credit_card_transaction_approved_message( \WC_Order $order, } // add the expiry date if it is available - if ( ! empty( $order->payment->exp_month ) && ! empty( $order->payment->exp_year ) ) { + if ( ! empty( $payment->exp_month ) && ! empty( $payment->exp_year ) ) { $message .= ' ' . sprintf( /* translators: Placeholder: %s - Credit card expiry date */ __( '(expires %s)', 'woocommerce-plugin-framework' ), - $order->payment->exp_month . '/' . substr( $order->payment->exp_year, -2 ) + $payment->exp_month . '/' . substr( $payment->exp_year, -2 ) ); } @@ -2883,16 +2919,18 @@ public function get_credit_card_transaction_approved_message( \WC_Order $order, */ public function get_echeck_transaction_approved_message( \WC_Order $order, SV_WC_Payment_Gateway_API_Response $response ) { - $last_four = ! empty( $order->payment->last_four ) ? $order->payment->last_four : substr( $order->payment->account_number, -4 ); + $payment = OrderHelper::get_payment( $order ); + + $last_four = ! empty( $payment->last_four ) ? $payment->last_four : substr( $payment->account_number, -4 ); // check order note. there may not be an account_type available, but that's fine /* translators: Context: "Check" as in "bank check" (noun, not verb). Placeholders: %1$s - payment method title, %2$s - payment account type (savings/checking) (may or may not be available), %3$s - last four digits of the account */ - $message = sprintf( __( '%1$s Check Transaction Approved: %2$s account ending in %3$s', 'woocommerce-plugin-framework' ), $this->get_method_title(), $order->payment->account_type, $last_four ); + $message = sprintf( __( '%1$s Check Transaction Approved: %2$s account ending in %3$s', 'woocommerce-plugin-framework' ), $this->get_method_title(), $payment->account_type, $last_four ); // optional check number - if ( ! empty( $order->payment->check_number ) ) { + if ( ! empty( $payment->check_number ) ) { /* translators: Context: "Check" as in "bank check" (noun, not verb). Placeholder: %s - check number */ - $message .= '. ' . sprintf( esc_html__( 'Check number %s', 'woocommerce-plugin-framework' ), $order->payment->check_number ); + $message .= '. ' . sprintf( esc_html__( 'Check number %s', 'woocommerce-plugin-framework' ), $payment->check_number ); } // adds the transaction id (if any) to the order note diff --git a/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-pre-orders.php b/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-pre-orders.php index 1fe186261..bdfb9afe8 100644 --- a/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-pre-orders.php +++ b/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-pre-orders.php @@ -24,6 +24,9 @@ namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers\OrderHelper; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Dynamic_Props; + defined( 'ABSPATH' ) or exit; if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v6_0_0\\SV_WC_Payment_Gateway_Integration_Pre_Orders' ) ) : @@ -139,44 +142,46 @@ public function get_order( $order ) { // normally a guest user wouldn't be assigned a customer id, but for a pre-order requiring tokenization, it might be if ( 0 == $order->get_user_id() && false !== ( $customer_id = $this->get_gateway()->get_guest_customer_id( $order ) ) ) { - $order->customer_id = $customer_id; + OrderHelper::set_customer_id( $order, $customer_id ); + } // zero out the payment total since we're just tokenizing the payment method - $order->payment_total = '0.00'; + OrderHelper::set_payment_total( $order, '0.00' ); } elseif ( \WC_Pre_Orders_Order::order_has_payment_token( $order ) && ! is_checkout_pay_page() ) { // if this is a pre-order release payment with a tokenized payment method, get the payment token to complete the order + $payment = OrderHelper::get_payment( $order ); // retrieve the payment token - $order->payment->token = $this->get_gateway()->get_order_meta( $order, 'payment_token' ); + $payment->token = $this->get_gateway()->get_order_meta( $order, 'payment_token' ); // retrieve the optional customer id - $order->customer_id = $this->get_gateway()->get_order_meta( $order, 'customer_id' ); + OrderHelper::set_customer_id( $order, $this->get_gateway()->get_order_meta( $order, 'customer_id' ) ); // set token data on order - if ( $this->get_gateway()->get_payment_tokens_handler()->user_has_token( $order->get_user_id(), $order->payment->token ) ) { + if ( $this->get_gateway()->get_payment_tokens_handler()->user_has_token( $order->get_user_id(), $payment->token ) ) { // an existing registered user with a saved payment token - $token = $this->get_gateway()->get_payment_tokens_handler()->get_token( $order->get_user_id(), $order->payment->token ); + $token = $this->get_gateway()->get_payment_tokens_handler()->get_token( $order->get_user_id(), $payment->token ); // account last four - $order->payment->account_number = $token->get_last_four(); + $payment->account_number = $token->get_last_four(); if ( $this->get_gateway()->is_credit_card_gateway() ) { // card type - $order->payment->card_type = $token->get_card_type(); + $payment->card_type = $token->get_card_type(); // exp month/year - $order->payment->exp_month = $token->get_exp_month(); - $order->payment->exp_year = SV_WC_Payment_Gateway_Helper::format_exp_year( $token->get_exp_year() ); + $payment->exp_month = $token->get_exp_month(); + $payment->exp_year = SV_WC_Payment_Gateway_Helper::format_exp_year( $token->get_exp_year() ); } elseif ( $this->get_gateway()->is_echeck_gateway() ) { // account type (checking/savings) - $order->payment->account_type = $token->get_account_type(); + $payment->account_type = $token->get_account_type(); } } else { @@ -184,28 +189,31 @@ public function get_order( $order ) { // a guest user means that token data must be set from the original order // account number - $order->payment->account_number = $this->get_gateway()->get_order_meta( $order, 'account_four' ); + $payment->account_number = $this->get_gateway()->get_order_meta( $order, 'account_four' ); if ( $this->get_gateway()->is_credit_card_gateway() ) { // card type - $order->payment->card_type = $this->get_gateway()->get_order_meta( $order, 'card_type' ); + $payment->card_type = $this->get_gateway()->get_order_meta( $order, 'card_type' ); // expiry date if ( $expiry_date = $this->get_gateway()->get_order_meta( $order, 'card_expiry_date' ) ) { list( $exp_year, $exp_month ) = explode( '-', $expiry_date ); - $order->payment->exp_month = $exp_month; - $order->payment->exp_year = SV_WC_Payment_Gateway_Helper::format_exp_year( $exp_year ); + $payment->exp_month = $exp_month; + $payment->exp_year = SV_WC_Payment_Gateway_Helper::format_exp_year( $exp_year ); } } elseif ( $this->get_gateway()->is_echeck_gateway() ) { // account type - $order->payment->account_type = $this->get_gateway()->get_order_meta( $order, 'account_type' ); + $payment->account_type = $this->get_gateway()->get_order_meta( $order, 'account_type' ); } } + + // Set payment info on the order object + OrderHelper::set_payment( $order, $payment ); } return $order; @@ -233,8 +241,10 @@ public function process_payment( $result, $order_id ) { try { + $payment = OrderHelper::get_payment( $order ); + // using an existing tokenized payment method - if ( isset( $order->payment->token ) && $order->payment->token ) { + if ( isset( $payment->token ) && $payment->token ) { // save the tokenized card info for completing the pre-order in the future $this->get_gateway()->add_transaction_data( $order ); @@ -302,18 +312,21 @@ public function process_release_payment( $order ) { try { // set order defaults - $order = $this->get_gateway()->get_order( $order ); + $order = $this->get_gateway()->get_order( $order ); + $payment = OrderHelper::get_payment( $order ); // order description - $order->description = sprintf( + $description = sprintf( /* translators: Context: A payment is released for a pre-order. Placeholders: %1$s - Site name, %2$s - Order number */ __( '%1$s - Pre-Order Release Payment for Order %2$s', 'woocommerce-plugin-framework' ), esc_html( SV_WC_Helper::get_site_name() ), $order->get_order_number() ); + Dynamic_Props::set( $order, 'description', $description ); + // token is required - if ( ! $order->payment->token ) { + if ( ! $payment->token ) { throw new SV_WC_Payment_Gateway_Exception( __( 'Payment token missing/invalid.', 'woocommerce-plugin-framework' ) ); } @@ -333,7 +346,7 @@ public function process_release_payment( $order ) { // success! update order record if ( $response->transaction_approved() ) { - $last_four = substr( $order->payment->account_number, -4 ); + $last_four = substr( $payment->account_number, -4 ); // order note based on gateway type if ( $this->get_gateway()->is_credit_card_gateway() ) { @@ -343,9 +356,9 @@ public function process_release_payment( $order ) { __( '%1$s %2$s Pre-Order Release Payment Approved: %3$s ending in %4$s (expires %5$s)', 'woocommerce-plugin-framework' ), $this->get_gateway()->get_method_title(), $this->get_gateway()->perform_credit_card_authorization( $order ) ? 'Authorization' : 'Charge', - SV_WC_Payment_Gateway_Helper::payment_type_to_name( ! empty( $order->payment->card_type ) ? $order->payment->card_type : 'card' ), + SV_WC_Payment_Gateway_Helper::payment_type_to_name( ! empty( $payment->card_type ) ? $payment->card_type : 'card' ), $last_four, - ( ! empty( $order->payment->exp_month) && ! empty( $order->payment->exp_year ) ? $order->payment->exp_month . '/' . substr( $order->payment->exp_year, -2 ) : 'n/a' ) + ( ! empty( $payment->exp_month) && ! empty( $payment->exp_year ) ? $payment->exp_month . '/' . substr( $payment->exp_year, -2 ) : 'n/a' ) ); } elseif ( $this->get_gateway()->is_echeck_gateway() ) { @@ -355,7 +368,7 @@ public function process_release_payment( $order ) { /* translators: Context: A payment is released for a pre-order. Placeholders: %1$s - Payment gateway name, %2$s - Payment method type (e.g. 'Bank Account'), %3$s - Last four digits of the account */ __( '%1$s eCheck Pre-Order Release Payment Approved: %2$s ending in %3$s', 'woocommerce-plugin-framework' ), $this->get_gateway()->get_method_title(), - SV_WC_Payment_Gateway_Helper::payment_type_to_name( ! empty( $order->payment->account_type ) ? $order->payment->account_type : 'bank'), + SV_WC_Payment_Gateway_Helper::payment_type_to_name( ! empty( $payment->account_type ) ? $payment->account_type : 'bank'), $last_four ); diff --git a/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-subscriptions.php b/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-subscriptions.php index ff5e33b55..b832f78ed 100644 --- a/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-subscriptions.php +++ b/woocommerce/payment-gateway/integrations/class-sv-wc-payment-gateway-integration-subscriptions.php @@ -24,6 +24,9 @@ namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers\OrderHelper; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Payment_Gateway\Dynamic_Props; + defined( 'ABSPATH' ) or exit; if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v6_0_0\\SV_WC_Payment_Gateway_Integration_Subscriptions' ) ) : @@ -216,14 +219,14 @@ public function save_payment_meta( $order ) { $updated = false; // payment token - if ( ! empty( $order->payment->token ) ) { - $subscription->update_meta_data( $this->get_gateway()->get_order_meta_prefix() . 'payment_token', $order->payment->token ); + if ( ! empty( Dynamic_Props::get( $order, 'payment', 'token' ) ) ) { + $subscription->update_meta_data( $this->get_gateway()->get_order_meta_prefix() . 'payment_token', Dynamic_Props::get( $order, 'payment', 'token' ) ); $updated = true; } // customer ID - if ( ! empty( $order->customer_id ) ) { - $subscription->update_meta_data( $this->get_gateway()->get_order_meta_prefix() . 'customer_id', $order->customer_id ); + if ( ! empty( OrderHelper::get_customer_id( $order ) ) ) { + $subscription->update_meta_data( $this->get_gateway()->get_order_meta_prefix() . 'customer_id', OrderHelper::get_customer_id( $order ) ); $updated = true; } @@ -249,16 +252,18 @@ public function save_payment_meta( $order ) { */ public function add_subscriptions_details_to_order( $order, $gateway ) { - if ( isset( $order->payment ) ) { + $payment = OrderHelper::get_payment( $order ); + + if ( $payment ) { // defaults - $order->payment->subscriptions = []; - $order->payment->recurring = ! empty( $order->payment->recurring ) ?: false; + $payment->subscriptions = []; + $payment->recurring = ! empty( $payment->recurring ) ?: false; // if the order contains a subscription (but is not a renewal) if ( wcs_order_contains_subscription( $order ) ) { - $order->payment->recurring = true; + $payment->recurring = true; // an order ID might be 0 if it's a mock order we use when adding a payment method // passing in an order with an ID of 0 to `wcs_get_subscriptions_for_order()` can cause very unexpected results @@ -270,7 +275,7 @@ public function add_subscriptions_details_to_order( $order, $gateway ) { if ( $subscription instanceof \WC_Subscription ) { - $order->payment->subscriptions[] = $this->add_subscription_details_to_order( $subscription, false ); + $payment->subscriptions[] = $this->add_subscription_details_to_order( $subscription, false ); } } } @@ -278,7 +283,7 @@ public function add_subscriptions_details_to_order( $order, $gateway ) { // order is for a subscription renewal } elseif ( wcs_order_contains_renewal( $order ) ) { - $order->payment->recurring = true; + $payment->recurring = true; // an order ID might be 0 if it's a mock order we use when adding a payment method // passing in an order with an ID of 0 to `wcs_get_subscriptions_for_order()` can cause very unexpected results @@ -290,11 +295,13 @@ public function add_subscriptions_details_to_order( $order, $gateway ) { if ( $subscription instanceof \WC_Subscription ) { - $order->payment->subscriptions[] = $this->add_subscription_details_to_order( $subscription, true ); + $payment->subscriptions[] = $this->add_subscription_details_to_order( $subscription, true ); } } } } + + OrderHelper::set_payment( $order, $payment ); } return $order; @@ -390,45 +397,52 @@ public function process_renewal_payment( $amount_to_charge, $order ) { */ public function get_order( $order ) { - $order->description = sprintf( + $description = sprintf( /* translators: Placeholders: %1$s - Site name, %2$s - Order number */ esc_html__( '%1$s - Subscription Renewal Order %2$s', 'woocommerce-plugin-framework' ), wp_specialchars_decode( SV_WC_Helper::get_site_name(), ENT_QUOTES ), $order->get_order_number() ); + Dynamic_Props::set( $order, 'description', $description ); + // override the payment total with the amount to charge given by Subscriptions - $order->payment_total = $this->renewal_payment_total; + OrderHelper::set_payment_total( $order, $this->renewal_payment_total ); + + $payment = OrderHelper::get_payment( $order ); // set payment token - $order->payment->token = $this->get_gateway()->get_order_meta( $order, 'payment_token' ); + $payment->token = $this->get_gateway()->get_order_meta( $order, 'payment_token' ); // use customer ID from renewal order, not user meta so the admin can update the customer ID for a subscription if needed $customer_id = $this->get_gateway()->get_order_meta( $order, 'customer_id' ); // only if a customer ID exists in order meta, otherwise this will default to the previously set value from user meta if ( ! empty( $customer_id ) ) { - $order->customer_id = $customer_id; + OrderHelper::set_customer_id( $order, $customer_id ); } // get the token object - $token = $this->get_gateway()->get_payment_tokens_handler()->get_token( $order->get_user_id(), $order->payment->token ); + $token = $this->get_gateway()->get_payment_tokens_handler()->get_token( $order->get_user_id(), $payment->token ); // set token data on the order - $order->payment->account_number = $token->get_last_four(); - $order->payment->last_four = $token->get_last_four(); + $payment->account_number = $token->get_last_four(); + $payment->last_four = $token->get_last_four(); if ( $token->is_credit_card() ) { - $order->payment->card_type = $token->get_card_type(); - $order->payment->exp_month = $token->get_exp_month(); - $order->payment->exp_year = SV_WC_Payment_Gateway_Helper::format_exp_year( $token->get_exp_year() ); + $payment->card_type = $token->get_card_type(); + $payment->exp_month = $token->get_exp_month(); + $payment->exp_year = SV_WC_Payment_Gateway_Helper::format_exp_year( $token->get_exp_year() ); } elseif ( $token->is_echeck() ) { - $order->payment->account_type = $token->get_account_type(); + $payment->account_type = $token->get_account_type(); } + // Set payment info on the order object + OrderHelper::set_payment( $order, $payment ); + return $order; } @@ -508,10 +522,12 @@ public function process_change_payment( $result, $order_id, $gateway ) { $subscription = $gateway->get_order( $order_id ); + $payment = OrderHelper::get_payment( $subscription ); + try { // if using a saved method, just add the data - if ( isset( $subscription->payment->token ) && $subscription->payment->token ) { + if ( isset( $payment->token ) && $payment->token ) { $gateway->add_transaction_data( $subscription ); diff --git a/woocommerce/payment-gateway/payment-tokens/class-sv-wc-payment-gateway-payment-tokens-handler.php b/woocommerce/payment-gateway/payment-tokens/class-sv-wc-payment-gateway-payment-tokens-handler.php index 439665c7c..f67c70b9a 100644 --- a/woocommerce/payment-gateway/payment-tokens/class-sv-wc-payment-gateway-payment-tokens-handler.php +++ b/woocommerce/payment-gateway/payment-tokens/class-sv-wc-payment-gateway-payment-tokens-handler.php @@ -24,6 +24,8 @@ namespace SkyVerge\WooCommerce\PluginFramework\v6_0_0; +use SkyVerge\WooCommerce\PluginFramework\v6_0_0\Helpers\OrderHelper; + defined( 'ABSPATH' ) or exit; if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v6_0_0\\SV_WC_Payment_Gateway_Payment_Tokens_Handler' ) ) : @@ -144,17 +146,19 @@ public function create_token( \WC_Order $order, $response = null, $environment_i // store the billing hash on the token for later use in case it needs to be updated $token->set_billing_hash( $address->get_hash() ); + $payment = OrderHelper::get_payment( $order ); + // set the resulting token on the order - $order->payment->token = $token->get_id(); + $payment->token = $token->get_id(); // for credit card transactions add the card type, if known (some gateways return the credit card type as part of the response, others may require it as part of the request, and still others it may never be known) if ( $gateway->is_credit_card_gateway() && $token->get_card_type() ) { - $order->payment->card_type = $token->get_card_type(); + $payment->card_type = $token->get_card_type(); } // checking/savings, if known if ( $gateway->is_echeck_gateway() && $token->get_account_type() ) { - $order->payment->account_type = $token->get_account_type(); + $payment->account_type = $token->get_account_type(); } // set the token to the user account @@ -162,6 +166,9 @@ public function create_token( \WC_Order $order, $response = null, $environment_i $this->add_token( $order->get_user_id(), $token, $environment_id ); } + // Set payment info on the order object. + OrderHelper::set_payment( $order, $payment ); + $order->add_order_note( $this->get_order_note( $token ) ); // add the standard transaction data