1514 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1514 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * 3.0 Data Migration - Data Migrator.
 | |
|  *
 | |
|  * @subpackage  Admin/Upgrades/v3
 | |
|  * @copyright   Copyright (c) 2018, Easy Digital Downloads, LLC
 | |
|  * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 | |
|  * @since       3.0
 | |
|  */
 | |
| namespace EDD\Admin\Upgrades\v3;
 | |
| 
 | |
| // Exit if accessed directly
 | |
| defined( 'ABSPATH' ) || exit;
 | |
| 
 | |
| /**
 | |
|  * Data_Migrator Class.
 | |
|  *
 | |
|  * This class holds all the logic for migrating data to custom tables as part
 | |
|  * of EDD 3.0.
 | |
|  *
 | |
|  * @since 3.0
 | |
|  */
 | |
| class Data_Migrator {
 | |
| 
 | |
| 	/**
 | |
| 	 * Customer addresses.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param object $data       Data to migrate.
 | |
| 	 * @param string $type       The type of address this is.
 | |
| 	 */
 | |
| 	public static function customer_addresses( $data = null, $type = 'billing' ) {
 | |
| 
 | |
| 		// Bail if no data passed.
 | |
| 		if ( ! $data ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$address = maybe_unserialize( $data->meta_value );
 | |
| 
 | |
| 		$user_id = absint( $data->user_id );
 | |
| 
 | |
| 		$customer = edd_get_customer_by( 'user_id', $user_id );
 | |
| 
 | |
| 		$address = wp_parse_args( $address, array(
 | |
| 			'line1'   => '',
 | |
| 			'line2'   => '',
 | |
| 			'city'    => '',
 | |
| 			'state'   => '',
 | |
| 			'zip'     => '',
 | |
| 			'country' => '',
 | |
| 		) );
 | |
| 
 | |
| 		$address_to_check = array_filter( $address );
 | |
| 
 | |
| 		// Do not migrate empty addresses.
 | |
| 		if ( empty( $address_to_check ) ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if ( $customer ) {
 | |
| 			edd_maybe_add_customer_address(
 | |
| 				array(
 | |
| 					'customer_id'  => $customer->id,
 | |
| 					'is_primary'   => true,
 | |
| 					'name'         => $customer->name,
 | |
| 					'address'      => $address['line1'],
 | |
| 					'address2'     => $address['line2'],
 | |
| 					'city'         => $address['city'],
 | |
| 					'region'       => $address['state'],
 | |
| 					'postal_code'  => $address['zip'],
 | |
| 					'country'      => $address['country'],
 | |
| 					'date_created' => $customer->date_created,
 | |
| 				)
 | |
| 			);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Customer email addresses.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param object $data Data to migrate.
 | |
| 	 */
 | |
| 	public static function customer_email_addresses( $data = null ) {
 | |
| 
 | |
| 		// Bail if no data passed.
 | |
| 		if ( ! isset( $data->edd_customer_id ) || ! isset( $data->meta_value ) ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$customer = edd_get_customer( absint( $data->edd_customer_id ) );
 | |
| 		if ( ! $customer ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		edd_add_customer_email_address(
 | |
| 			array(
 | |
| 				'customer_id'  => $customer->id,
 | |
| 				'email'        => $data->meta_value,
 | |
| 				'date_created' => $customer->date_created,
 | |
| 			)
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Customer notes.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param object $data Data to migrate.
 | |
| 	 */
 | |
| 	public static function customer_notes( $data = null ) {
 | |
| 
 | |
| 		// Bail if no data passed.
 | |
| 		if ( ! $data ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$customer_id = absint( $data->id );
 | |
| 
 | |
| 		if ( property_exists( $data, 'notes' ) && ! empty( $data->notes ) ) {
 | |
| 			$notes = array_reverse( array_filter( explode( "\n\n", $data->notes ) ) );
 | |
| 
 | |
| 			$notes = array_map( function( $val ) {
 | |
| 				return explode( ' - ', $val );
 | |
| 			}, $notes );
 | |
| 
 | |
| 			if ( ! empty( $notes ) ) {
 | |
| 				foreach ( $notes as $note ) {
 | |
| 					try {
 | |
| 						$date = isset( $note[0] )
 | |
| 							? EDD()->utils->date( $note[0], edd_get_timezone_id() )->setTimezone( 'UTC' )->toDateTimeString()
 | |
| 							: '';
 | |
| 					} catch ( \Exception $e ) {
 | |
| 						// An empty date will be changed to current time in BerlinDB.
 | |
| 						$date = '';
 | |
| 					}
 | |
| 
 | |
| 					$note_content = isset( $note[1] )
 | |
| 						? $note[1]
 | |
| 						: '';
 | |
| 
 | |
| 					edd_add_note( array(
 | |
| 						'user_id'       => 0,
 | |
| 						'object_id'     => $customer_id,
 | |
| 						'object_type'   => 'customer',
 | |
| 						'content'       => $note_content,
 | |
| 						'date_created'  => $date,
 | |
| 						'date_modified' => $date,
 | |
| 					) );
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Discounts.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param object $data Data to migrate.
 | |
| 	 */
 | |
| 	public static function discounts( $data = null ) {
 | |
| 
 | |
| 		// Bail if no data passed.
 | |
| 		if ( ! $data ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$data = get_post( $data->ID );
 | |
| 
 | |
| 		$args            = array();
 | |
| 		$meta            = get_post_custom( $data->ID );
 | |
| 		$meta_to_migrate = array();
 | |
| 		$core_meta       = array(
 | |
| 			'code',
 | |
| 			'name',
 | |
| 			'status',
 | |
| 			'uses',
 | |
| 			'max_uses',
 | |
| 			'amount',
 | |
| 			'start',
 | |
| 			'expiration',
 | |
| 			'type',
 | |
| 			'min_price',
 | |
| 			'product_reqs',
 | |
| 			'product_condition',
 | |
| 			'excluded_products',
 | |
| 			'is_not_global',
 | |
| 			'is_single_use',
 | |
| 		);
 | |
| 
 | |
| 		foreach ( $meta as $key => $value ) {
 | |
| 			$value = maybe_unserialize( $value[0] );
 | |
| 			if ( false === strpos( $key, '_edd_discount' ) ) {
 | |
| 
 | |
| 				// This is custom meta from another plugin that needs to be migrated to the new meta table.
 | |
| 				$meta_to_migrate[ $key ] = $value;
 | |
| 				continue;
 | |
| 			}
 | |
| 			$meta_key = str_replace( '_edd_discount_', '', $key );
 | |
| 			if ( ! in_array( $meta_key, $core_meta, true ) ) {
 | |
| 				$meta_to_migrate[ $meta_key ] = $value;
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			$args[ $meta_key ] = $value;
 | |
| 		}
 | |
| 
 | |
| 		// If the discount name was not stored in post_meta, use value from the WP_Post object.
 | |
| 		if ( ! isset( $args['name'] ) ) {
 | |
| 			$args['name'] = $data->post_title;
 | |
| 		}
 | |
| 
 | |
| 		$args['id']            = $data->ID;
 | |
| 		$args['date_created']  = $data->post_date_gmt;
 | |
| 		$args['date_modified'] = $data->post_modified_gmt;
 | |
| 
 | |
| 		// Use edd_store_discount() so any legacy data is handled correctly.
 | |
| 		$discount_id = edd_store_discount( $args );
 | |
| 
 | |
| 		// Migrate any additional meta.
 | |
| 		if ( ! empty( $meta_to_migrate ) ) {
 | |
| 			foreach ( $meta_to_migrate as $key => $value ) {
 | |
| 				edd_add_adjustment_meta( $discount_id, $key, $value );
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Logs.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param object $data Data to migrate.
 | |
| 	 */
 | |
| 	public static function logs( $data = null ) {
 | |
| 		global $wpdb;
 | |
| 
 | |
| 		// Bail if no data passed.
 | |
| 		if ( ! $data ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$meta_to_migrate = array();
 | |
| 		if ( 'file_download' === $data->slug ) {
 | |
| 			$meta = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id = %d", absint( $data->ID ) ) );
 | |
| 
 | |
| 			$post_meta = array();
 | |
| 
 | |
| 			foreach ( $meta as $meta_item ) {
 | |
| 				$post_meta[ $meta_item->meta_key ] = maybe_unserialize( $meta_item->meta_value );
 | |
| 			}
 | |
| 
 | |
| 			$log_data = array(
 | |
| 				'product_id'    => $data->post_parent,
 | |
| 				/*
 | |
| 				 * Custom Deliverables was overriding the file ID to be a string instead of an integer. The preg_replace
 | |
| 				 * allows us to try to salvage the file ID from that string.
 | |
| 				 */
 | |
| 				'file_id'       => isset( $post_meta['_edd_log_file_id'] ) ? preg_replace( '/[^0-9]/', '', $post_meta['_edd_log_file_id'] ) : 0,
 | |
| 				'order_id'      => isset( $post_meta['_edd_log_payment_id'] )  ? $post_meta['_edd_log_payment_id']  : 0,
 | |
| 				'price_id'      => isset( $post_meta['_edd_log_price_id'] )    ? $post_meta['_edd_log_price_id']    : 0,
 | |
| 				'customer_id'   => isset( $post_meta['_edd_log_customer_id'] ) ? $post_meta['_edd_log_customer_id'] : 0,
 | |
| 				'ip'            => isset( $post_meta['_edd_log_ip'] ) ? $post_meta['_edd_log_ip'] : '',
 | |
| 				'date_created'  => $data->post_date_gmt,
 | |
| 				'date_modified' => $data->post_modified_gmt,
 | |
| 			);
 | |
| 
 | |
| 			$meta_to_remove = array(
 | |
| 				'_edd_log_file_id',
 | |
| 				'_edd_log_payment_id',
 | |
| 				'_edd_log_price_id',
 | |
| 				'_edd_log_customer_id',
 | |
| 				'_edd_log_ip',
 | |
| 				'_edd_log_user_id',
 | |
| 			);
 | |
| 			// If the log doesn't have a customer ID, but does have a user ID, keep the user ID as metadata.
 | |
| 			if ( empty( $log_data['customer_id'] ) && ! empty( $post_meta['_edd_log_user_id'] ) && ! in_array( $post_meta['_edd_log_user_id'], array( 0, -1 ) ) ) {
 | |
| 				$meta_to_remove = array_diff( $meta_to_remove, array( '_edd_log_user_id' ) );
 | |
| 			}
 | |
| 			$meta_to_migrate   = $post_meta;
 | |
| 			$new_log_id        = edd_add_file_download_log( $log_data );
 | |
| 			$add_meta_function = 'edd_add_file_download_log_meta';
 | |
| 
 | |
| 			/**
 | |
| 			 * Triggers after a file download log has been migrated.
 | |
| 			 *
 | |
| 			 * @since 3.0
 | |
| 			 *
 | |
| 			 * @param int    $new_log_id ID of the newly created log.
 | |
| 			 * @param object $data       Data from the posts table. (Essentially a `WP_Post`, without being that object.)
 | |
| 			 * @param array  $post_meta  All meta associated with this log.
 | |
| 			 */
 | |
| 			do_action( 'edd_30_migrate_file_download_log', $new_log_id, $data, $post_meta );
 | |
| 		} elseif ( 'api_request' === $data->slug ) {
 | |
| 			$meta = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id = %d", absint( $data->ID ) ) );
 | |
| 
 | |
| 			$post_meta = array();
 | |
| 
 | |
| 			foreach ( $meta as $meta_item ) {
 | |
| 				$post_meta[ $meta_item->meta_key ] = maybe_unserialize( $meta_item->meta_value );
 | |
| 			}
 | |
| 
 | |
| 			$post_meta = wp_parse_args(
 | |
| 				$post_meta,
 | |
| 				array(
 | |
| 					'_edd_log_request_ip' => '',
 | |
| 					'_edd_log_user'       => 0,
 | |
| 					'_edd_log_key'        => 'public',
 | |
| 					'_edd_log_token'      => 'public',
 | |
| 					'_edd_log_version'    => '',
 | |
| 					'_edd_log_time'       => '',
 | |
| 				)
 | |
| 			);
 | |
| 
 | |
| 			if ( empty( $post_meta['_edd_log_token'] ) ) {
 | |
| 				$post_meta['_edd_log_token'] = 'public' === $post_meta['_edd_log_key'] ? 'public' : '';
 | |
| 			}
 | |
| 
 | |
| 			$log_data = array(
 | |
| 				'ip'            => $post_meta['_edd_log_request_ip'],
 | |
| 				'user_id'       => $post_meta['_edd_log_user'],
 | |
| 				'api_key'       => $post_meta['_edd_log_key'],
 | |
| 				'token'         => $post_meta['_edd_log_token'],
 | |
| 				'version'       => $post_meta['_edd_log_version'],
 | |
| 				'time'          => $post_meta['_edd_log_time'],
 | |
| 				'request'       => $data->post_excerpt,
 | |
| 				'error'         => $data->post_content,
 | |
| 				'date_created'  => $data->post_date_gmt,
 | |
| 				'date_modified' => $data->post_modified_gmt,
 | |
| 			);
 | |
| 
 | |
| 			$meta_to_remove    = array(
 | |
| 				'_edd_log_request_ip',
 | |
| 				'_edd_log_user',
 | |
| 				'_edd_log_key',
 | |
| 				'_edd_log_token',
 | |
| 				'_edd_log_version',
 | |
| 				'_edd_log_time',
 | |
| 			);
 | |
| 			$meta_to_migrate   = $post_meta;
 | |
| 			$new_log_id        = edd_add_api_request_log( $log_data );
 | |
| 			$add_meta_function = 'edd_add_api_request_log_meta';
 | |
| 		} else {
 | |
| 			$post_meta = get_post_custom( $data->ID );
 | |
| 			foreach ( $post_meta as $key => $value ) {
 | |
| 				$meta_to_migrate[ $key ] = maybe_unserialize( $value[0] );
 | |
| 			}
 | |
| 
 | |
| 			$log_data = array(
 | |
| 				'object_id'     => $data->post_parent,
 | |
| 				'object_type'   => 'download',
 | |
| 				'user_id'       => ! empty( $meta_to_migrate['_edd_log_user'] ) ? $meta_to_migrate['_edd_log_user'] : $data->post_author,
 | |
| 				'type'          => $data->slug,
 | |
| 				'title'         => $data->post_title,
 | |
| 				'content'       => $data->post_content,
 | |
| 				'date_created'  => $data->post_date_gmt,
 | |
| 				'date_modified' => $data->post_modified_gmt,
 | |
| 			);
 | |
| 
 | |
| 			$meta_to_remove = array(
 | |
| 				'_edit_lock',
 | |
| 				'_edd_log_user',
 | |
| 			);
 | |
| 
 | |
| 			$new_log_id        = edd_add_log( $log_data );
 | |
| 			$add_meta_function = 'edd_add_log_meta';
 | |
| 		}
 | |
| 
 | |
| 		if ( ! is_callable( $add_meta_function ) || empty( $meta_to_migrate ) ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		foreach ( $meta_to_migrate as $key => $value ) {
 | |
| 			if ( ! in_array( $key, $meta_to_remove, true ) ) {
 | |
| 				// Strip off `_edd_log_` prefix.
 | |
| 				$key = str_replace( '_edd_log_', '', $key );
 | |
| 
 | |
| 				$add_meta_function( $new_log_id, $key, $value );
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Order notes.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param object $data Data to migrate.
 | |
| 	 */
 | |
| 	public static function order_notes( $data = null ) {
 | |
| 
 | |
| 		// Bail if no data passed.
 | |
| 		if ( ! $data ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$note_data = array(
 | |
| 			'object_id'     => $data->object_id,
 | |
| 			'object_type'   => 'order',
 | |
| 			'date_created'  => $data->comment_date_gmt,
 | |
| 			'date_modified' => $data->comment_date_gmt,
 | |
| 			'content'       => $data->comment_content,
 | |
| 			'user_id'       => $data->user_id,
 | |
| 		);
 | |
| 
 | |
| 		$id = edd_add_note( $note_data );
 | |
| 
 | |
| 		$meta = get_comment_meta( $data->comment_ID );
 | |
| 		if ( ! empty( $meta ) ) {
 | |
| 			foreach ( $meta as $key => $value ) {
 | |
| 				edd_add_note_meta( $id, $key, $value );
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static function orders( $data = null ) {
 | |
| 
 | |
| 		// Bail if no data passed.
 | |
| 		if ( ! $data ) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		/** Create a new order ***************************************/
 | |
| 		global $wpdb;
 | |
| 
 | |
| 		// Get's all the post meta for this payment.
 | |
| 		$meta = get_post_custom( $data->ID );
 | |
| 
 | |
| 		$payment_meta = maybe_unserialize( $meta['_edd_payment_meta'][0] );
 | |
| 		$user_info    = isset( $payment_meta['user_info'] ) ? maybe_unserialize( $payment_meta['user_info'] ) : array();
 | |
| 
 | |
| 		// Some old EDD data has the user info serialized, but starting with something other than a: so it can't be unserialized
 | |
| 		$user_info = self::fix_possible_serialization( $user_info );
 | |
| 		$user_info = maybe_unserialize( $user_info );
 | |
| 
 | |
| 		if ( ! is_array( $user_info ) ) {
 | |
| 			$user_info = array();
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * Last chance to filter payment meta before we use it!
 | |
| 		 * Note: If modifying `cart_details`, then it's recommended that you first run
 | |
| 		 * `EDD\Admin\Upgrades\v3\Data_Migrator::fix_possible_serialization()`
 | |
| 		 * before making adjustments.
 | |
| 		 *
 | |
| 		 * @since 3.0
 | |
| 		 *
 | |
| 		 * @param array $payment_meta Payment meta.
 | |
| 		 * @param int   $payment_id   ID of the payment.
 | |
| 		 * @param array $meta         All post meta.
 | |
| 		 */
 | |
| 		$payment_meta = apply_filters( 'edd_30_migration_payment_meta', $payment_meta, $data->ID, $meta );
 | |
| 
 | |
| 		$order_number   = isset( $meta['_edd_payment_number'][0] ) ? $meta['_edd_payment_number'][0] : '';
 | |
| 		$user_id        = isset( $meta['_edd_payment_user_id'][0] ) && ! empty( $meta['_edd_payment_user_id'][0] ) ? $meta['_edd_payment_user_id'][0] : 0;
 | |
| 		$ip             = isset( $meta['_edd_payment_user_ip'][0] ) ? $meta['_edd_payment_user_ip'][0] : '';
 | |
| 		$mode           = isset( $meta['_edd_payment_mode'][0] ) ? $meta['_edd_payment_mode'][0] : 'live';
 | |
| 		$gateway        = isset( $meta['_edd_payment_gateway'][0] ) && ! empty( $meta['_edd_payment_gateway'][0] ) ? $meta['_edd_payment_gateway'][0] : 'manual';
 | |
| 		$customer_id    = isset( $meta['_edd_payment_customer_id'][0] ) ? $meta['_edd_payment_customer_id'][0] : 0;
 | |
| 		$date_completed = isset( $meta['_edd_completed_date'][0] ) ? $meta['_edd_completed_date'][0] : null;
 | |
| 		$purchase_key   = isset( $meta['_edd_payment_purchase_key'][0]) ? $meta['_edd_payment_purchase_key'][0] : false;
 | |
| 		$purchase_email = isset( $meta['_edd_payment_user_email'][0] ) ? $meta['_edd_payment_user_email'][0] : $payment_meta['email'];
 | |
| 
 | |
| 		// Get the customer object
 | |
| 		if ( ! empty( $customer_id ) ) {
 | |
| 			$customer = edd_get_customer( $customer_id );
 | |
| 		} else if ( ! empty( $purchase_email ) ) {
 | |
| 			$customer = edd_get_customer_by( 'email', $purchase_email );
 | |
| 			if ( $customer ) {
 | |
| 				$customer_id = $customer->id;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ( false === $purchase_key ) {
 | |
| 			$purchase_key = isset( $payment_meta['key'] ) ? $payment_meta['key'] : '';
 | |
| 		}
 | |
| 
 | |
| 		// Do not use -1 as the user ID.
 | |
| 		$user_id = ( -1 === $user_id )
 | |
| 			? 0
 | |
| 			: $user_id;
 | |
| 
 | |
| 		// Account for possible double serialization of the cart_details
 | |
| 		$cart_details = isset( $payment_meta['cart_details'] ) ? maybe_unserialize( $payment_meta['cart_details'] ) : array();
 | |
| 
 | |
| 		// Some old EDD data has the cart details serialized, but starting with something other than a: so it can't be unserialized
 | |
| 		$cart_details = self::fix_possible_serialization( $cart_details );
 | |
| 
 | |
| 		// Some old cart data does not contain subtotal or discount information. Normalize it.
 | |
| 		$cart_details = self::normalize_cart_details( $cart_details );
 | |
| 
 | |
| 		// Account for possible double serialization of the cart_details
 | |
| 		$cart_downloads = isset( $payment_meta['downloads'] ) ? maybe_unserialize( $payment_meta['downloads'] ) : array();
 | |
| 
 | |
| 		// Some old EDD data has the downloads serialized, but starting with something other than a: so it can't be unserialized
 | |
| 		$cart_downloads = self::fix_possible_serialization( $cart_downloads );
 | |
| 
 | |
| 		// If the order status is 'publish' convert it to the new 'complete' status.
 | |
| 		$order_status = 'publish' === $data->post_status ? 'complete' : $data->post_status;
 | |
| 
 | |
| 		// If there are no items, and it's abandoned, just return, since this isn't a valid order.
 | |
| 		if ( 'abandoned' === $order_status && empty( $cart_downloads ) && empty( $cart_details ) ) {
 | |
| 			edd_debug_log( 'Skipping order ' . $data->ID . ' due to abandoned status and no products.', true );
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		$order_subtotal = 0;
 | |
| 		$order_tax      = 0;
 | |
| 		$order_discount = 0;
 | |
| 		$order_total    = 0;
 | |
| 
 | |
| 		// Track the total value of added fees in case the Order was initially migrated
 | |
| 		// without _edd_payment_total or _edd_payment_tax and manual calculation was needed.
 | |
| 		$order_fees_tax       = 0;
 | |
| 		$order_fees_total     = 0;
 | |
| 		$order_items_fees_tax = 0;
 | |
| 
 | |
| 		// Retrieve the tax amount from metadata if available.
 | |
| 		$meta_tax = isset( $meta['_edd_payment_tax'] )
 | |
| 			? $meta['_edd_payment_tax']
 | |
| 			: false;
 | |
| 
 | |
| 		if ( false !== $meta_tax ) {
 | |
| 			$meta_tax  = maybe_unserialize( $meta_tax );
 | |
| 			$order_tax = (float) $meta_tax[0];
 | |
| 		}
 | |
| 
 | |
| 		$meta_total = false;
 | |
| 		// Retrieve the total amount from metadata if available.
 | |
| 		if ( isset( $meta['_edd_payment_total'] ) ) {
 | |
| 			$meta_total  = maybe_unserialize( $meta['_edd_payment_total'] );
 | |
| 			$order_total = (float) $meta_total[0];
 | |
| 		} elseif ( isset( $payment_meta['amount'] ) ) {
 | |
| 			$meta_total  = maybe_unserialize( $payment_meta['amount'] );
 | |
| 			$order_total = (float) $meta_total;
 | |
| 		}
 | |
| 
 | |
| 		// In some cases (very few) there is no cart details...so we have to just avoid this part.
 | |
| 		if ( ! empty( $cart_details ) && is_array( $cart_details ) ) {
 | |
| 
 | |
| 			// Loop through the items in the purchase to build the totals.
 | |
| 			foreach ( $cart_details as $cart_item ) {
 | |
| 				$order_subtotal += $cart_item['subtotal'];
 | |
| 
 | |
| 				// Add the cart line item tax amount if a total is not available on the order.
 | |
| 				if ( false === $meta_tax ) {
 | |
| 					$order_tax += $cart_item['tax'];
 | |
| 				}
 | |
| 
 | |
| 				$order_discount += $cart_item['discount'];
 | |
| 
 | |
| 				// Add the cart line item price amount (includes tax, order item fee, _but not order item fee tax_)
 | |
| 				// if a total is not available on the order.
 | |
| 				if ( false === $meta_total ) {
 | |
| 					$order_total += $cart_item['price'];
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		// Account for a situation where the post_date_gmt is set to 0000-00-00 00:00:00
 | |
| 		$date_created_gmt = $data->post_date_gmt;
 | |
| 		if ( '0000-00-00 00:00:00' === $date_created_gmt ) {
 | |
| 
 | |
| 			$date_created_gmt  = new \DateTime( $data->post_date );
 | |
| 			$modified_time     = new \DateTime( $data->post_modified );
 | |
| 			$modified_time_gmt = new \DateTime( $data->post_modified_gmt );
 | |
| 
 | |
| 			if ( $modified_time != $modified_time_gmt ) {
 | |
| 				$diff = $modified_time_gmt->diff( $modified_time );
 | |
| 
 | |
| 				$time_diff = 'PT';
 | |
| 
 | |
| 				// Add hours to the offset string.
 | |
| 				if ( ! empty( $diff->h ) ) {
 | |
| 					$time_diff .= $diff->h . 'H';
 | |
| 				}
 | |
| 
 | |
| 				// Add minutes to the offset string.
 | |
| 				if ( ! empty( $diff->i ) ) {
 | |
| 					$time_diff .= $diff->i . 'M';
 | |
| 				}
 | |
| 
 | |
| 				// Account for -/+ GMT offsets.
 | |
| 				try {
 | |
| 					if ( 1 === $diff->invert ) {
 | |
| 						$date_created_gmt->add( new \DateInterval( $time_diff ) );
 | |
| 					} else {
 | |
| 						$date_created_gmt->sub( new \DateInterval( $time_diff ) );
 | |
| 					}
 | |
| 				} catch ( \Exception $e ) {
 | |
| 
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			$date_created_gmt = $date_created_gmt->format('Y-m-d H:i:s');
 | |
| 		}
 | |
| 
 | |
| 		// Maybe convert the date completed to UTC or backfill the date_completed.
 | |
| 		$non_completed_statuses = apply_filters( 'edd_30_noncomplete_statuses', edd_get_incomplete_order_statuses() );
 | |
| 		if ( ! in_array( $order_status, $non_completed_statuses, true ) ) {
 | |
| 
 | |
| 			if ( ! empty( $date_completed ) ) {  // Update the data_completed to the UTC.
 | |
| 				try {
 | |
| 					$date_completed = EDD()->utils->date( $date_completed, edd_get_timezone_id() )->setTimezone( 'UTC' )->toDateTimeString();
 | |
| 				} catch ( \Exception $e ) {
 | |
| 					$date_completed = $date_created_gmt;
 | |
| 				}
 | |
| 			} elseif ( is_null( $date_completed ) ) { // Backfill a missing date_completed (for things like recurring payments).
 | |
| 				$date_completed = $date_created_gmt;
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		if ( 'manual_purchases' === $gateway && isset( $meta['_edd_payment_total'][0] ) ) {
 | |
| 			$gateway     = 'manual';
 | |
| 			$order_total = $meta['_edd_payment_total'][0];
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * Build up the order address data. Actual insertion happens later, but we need this now to figure out the tax rate.
 | |
| 		 */
 | |
| 
 | |
| 		// First & last name.
 | |
| 		$user_info['first_name'] = ! empty( $user_info['first_name'] )
 | |
| 			? $user_info['first_name']
 | |
| 			: '';
 | |
| 		$user_info['last_name']  = ! empty( $user_info['last_name'] )
 | |
| 			? $user_info['last_name']
 | |
| 			: '';
 | |
| 
 | |
| 		// Add order address.
 | |
| 		$user_info['address'] = ! empty( $user_info['address'] )
 | |
| 			? $user_info['address']
 | |
| 			: array();
 | |
| 
 | |
| 		$user_info['address'] = wp_parse_args( $user_info['address'], array(
 | |
| 			'line1'   => '',
 | |
| 			'line2'   => '',
 | |
| 			'city'    => '',
 | |
| 			'zip'     => '',
 | |
| 			'country' => '',
 | |
| 			'state'   => '',
 | |
| 		) );
 | |
| 
 | |
| 		$order_address_data = array(
 | |
| 			'name'         => trim( $user_info['first_name'] . ' ' . $user_info['last_name'] ),
 | |
| 			'address'      => isset( $user_info['address']['line1'] )   ? $user_info['address']['line1']   : '',
 | |
| 			'address2'     => isset( $user_info['address']['line2'] )   ? $user_info['address']['line2']   : '',
 | |
| 			'city'         => isset( $user_info['address']['city'] )    ? $user_info['address']['city']    : '',
 | |
| 			'region'       => isset( $user_info['address']['state'] )   ? $user_info['address']['state']   : '',
 | |
| 			'country'      => isset( $user_info['address']['country'] ) && array_key_exists( strtoupper( $user_info['address']['country'] ), edd_get_country_list() )
 | |
| 				? $user_info['address']['country']
 | |
| 				: '',
 | |
| 			'postal_code'  => isset( $user_info['address']['zip'] )     ? $user_info['address']['zip']     : '',
 | |
| 			'date_created' => $date_created_gmt,
 | |
| 		);
 | |
| 
 | |
| 		$tax_rate_id = null;
 | |
| 		$tax_rate = isset( $meta['_edd_payment_tax_rate'][0] )
 | |
| 			? (float) $meta['_edd_payment_tax_rate'][0]
 | |
| 			: 0.00;
 | |
| 
 | |
| 		/*
 | |
| 		 * Previously tax rates were stored as a decimal (e.g. `0.2`) but they're now stored as a percentage
 | |
| 		 * (e.g. `20`). So we need to convert.
 | |
| 		 */
 | |
| 		if ( $tax_rate < 1 ) {
 | |
| 			$tax_rate = $tax_rate * 100;
 | |
| 		}
 | |
| 
 | |
| 		$set_tax_rate_meta = false;
 | |
| 
 | |
| 		if ( ! empty( $tax_rate ) ) {
 | |
| 			// Fetch the actual tax rate object for the order region & country.
 | |
| 			$tax_rate_object = edd_get_tax_rate_by_location( array(
 | |
| 				'country' => $order_address_data['country'],
 | |
| 				'region'  => $order_address_data['region'],
 | |
| 			) );
 | |
| 
 | |
| 			if ( ! empty( $tax_rate_object->id ) && $tax_rate_object->amount == $tax_rate ) {
 | |
| 				$tax_rate_id = $tax_rate_object->id;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * If we cannot find a matching Adjustment object, we should save this in order meta so it isn't lost.
 | |
| 		 */
 | |
| 		if ( ! empty( $tax_rate ) && empty( $tax_rate_id ) ) {
 | |
| 			$set_tax_rate_meta = true;
 | |
| 		}
 | |
| 
 | |
| 		// Build the order data before inserting.
 | |
| 		$order_data = array(
 | |
| 			'id'             => $data->ID,
 | |
| 			'parent'         => $data->post_parent,
 | |
| 			'order_number'   => $order_number,
 | |
| 			'status'         => $order_status,
 | |
| 			'type'           => 'sale',
 | |
| 			'date_created'   => $date_created_gmt, // GMT is stored in the database as the offset is applied by the new query classes.
 | |
| 			'date_modified'  => $data->post_modified_gmt, // GMT is stored in the database as the offset is applied by the new query classes.
 | |
| 			'date_completed' => $date_completed,
 | |
| 			'user_id'        => $user_id,
 | |
| 			'customer_id'    => $customer_id,
 | |
| 			'email'          => $purchase_email,
 | |
| 			'ip'             => $ip,
 | |
| 			'gateway'        => $gateway,
 | |
| 			'mode'           => $mode,
 | |
| 			'currency'       => ! empty( $payment_meta['currency'] ) ? $payment_meta['currency'] : edd_get_currency(),
 | |
| 			'payment_key'    => $purchase_key,
 | |
| 			'tax_rate_id'    => $tax_rate_id,
 | |
| 			'subtotal'       => $order_subtotal,
 | |
| 			'tax'            => $order_tax,
 | |
| 			'discount'       => $order_discount,
 | |
| 			'total'          => $order_total,
 | |
| 		);
 | |
| 
 | |
| 		/**
 | |
| 		 * Filters the data used to create the order.
 | |
| 		 *
 | |
| 		 * @since 3.0
 | |
| 		 *
 | |
| 		 * @param array $order_data   Order creation arguments.
 | |
| 		 * @param array $payment_meta Payment meta.
 | |
| 		 * @param array $cart_details Cart details.
 | |
| 		 * @param array $meta         All payment meta.
 | |
| 		 */
 | |
| 		$order_data = apply_filters( 'edd_30_migration_order_creation_data', $order_data, $payment_meta, $cart_details, $meta );
 | |
| 
 | |
| 		// Remove all order status transition actions.
 | |
| 		remove_all_actions( 'edd_transition_order_status' );
 | |
| 		remove_all_actions( 'edd_transition_order_item_status' );
 | |
| 		remove_action( 'edd_order_item_added', 'edd_recalculate_order_item_download' );
 | |
| 		remove_action( 'edd_order_item_updated', 'edd_recalculate_order_item_download' );
 | |
| 		remove_action( 'edd_order_item_deleted', 'edd_recalculate_order_item_download' );
 | |
| 		remove_action( 'edd_order_adjustment_added', 'edd_recalculate_order_adjustment_download' );
 | |
| 		remove_action( 'edd_order_adjustment_updated', 'edd_recalculate_order_adjustment_download' );
 | |
| 
 | |
| 		$order_id = edd_add_order( $order_data );
 | |
| 
 | |
| 		// Save an un-matched tax rate in order meta.
 | |
| 		if ( $set_tax_rate_meta ) {
 | |
| 			edd_add_order_meta( $order_id, 'tax_rate', $tax_rate );
 | |
| 		}
 | |
| 
 | |
| 		// Do not pass the original order ID into other arrays
 | |
| 		unset( $order_data['id'] );
 | |
| 
 | |
| 		// Reset the $refund_id variable so that we don't end up accidentally creating refunds.
 | |
| 		$refund_id = 0;
 | |
| 
 | |
| 		// If the order status is 'refunded', we need to generate a new order with the type of 'refund'.
 | |
| 		if ( 'refunded' === $order_status ) {
 | |
| 
 | |
| 			// Since the refund is a near copy of the original order, copy over the arguments.
 | |
| 			$refund_data = $order_data;
 | |
| 
 | |
| 			$refund_data['parent']       = $order_id;
 | |
| 			$refund_data['order_number'] = $order_id . apply_filters( 'edd_order_refund_suffix', '-R-' ) . '1';
 | |
| 			$refund_data['type']         = 'refund';
 | |
| 			$refund_data['status']       = 'complete';
 | |
| 
 | |
| 			// Negate the amounts
 | |
| 			$refund_data['subtotal'] = edd_negate_amount( $order_subtotal );
 | |
| 			$refund_data['tax']      = edd_negate_amount( $order_tax );
 | |
| 			$refund_data['discount'] = edd_negate_amount( $order_discount );
 | |
| 			$refund_data['total']    = edd_negate_amount( $order_total );
 | |
| 
 | |
| 
 | |
| 			// These are the best guess at the date it was refunded since we didn't store that prior.
 | |
| 			$refund_data['date_created']  = $data->post_modified_gmt;
 | |
| 			$refund_data['date_modified'] = $data->post_modified_gmt;
 | |
| 
 | |
| 			$refund_id = edd_add_order( $refund_data );
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		// Remove empty data.
 | |
| 		$order_address_data = array_filter( $order_address_data );
 | |
| 		if ( ! empty( $order_address_data ) ) {
 | |
| 			// Add to edd_order_addresses table.
 | |
| 			$order_address_data['order_id'] = $order_id;
 | |
| 			edd_add_order_address( $order_address_data );
 | |
| 		}
 | |
| 
 | |
| 		// Maybe add the address to the edd_customer_addresses.
 | |
| 		$customer_address_data = $order_address_data;
 | |
| 
 | |
| 		// We don't need to pass this data to edd_maybe_add_customer_address().
 | |
| 		unset( $customer_address_data['order_id'] );
 | |
| 		unset( $customer_address_data['first_name'] );
 | |
| 		unset( $customer_address_data['last_name'] );
 | |
| 
 | |
| 		// If possible, set the order date as the address creation date.
 | |
| 		$customer_address_data['date_created'] = $date_created_gmt;
 | |
| 
 | |
| 		// Maybe add address to customer record.
 | |
| 		edd_maybe_add_customer_address( $customer_id, $customer_address_data );
 | |
| 
 | |
| 		// Maybe add email address to customer record
 | |
| 		if ( ! empty( $customer ) && $customer instanceof \EDD_Customer ) {
 | |
| 			$type = ( $customer->email === $purchase_email ) ? 'primary' : 'secondary';
 | |
| 			edd_add_customer_email_address(
 | |
| 				array(
 | |
| 					'customer_id'  => $customer_id,
 | |
| 					'date_created' => $date_created_gmt,
 | |
| 					'email'        => $purchase_email,
 | |
| 					'type'         => $type,
 | |
| 				)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		/** Migrate meta *********************************************/
 | |
| 
 | |
| 		// Unlimited downloads meta is not an order property, so we set it on the order meta for the new order ID.
 | |
| 		if ( isset( $meta['_edd_payment_unlimited_downloads'] ) && ! empty( $meta['_edd_payment_unlimited_downloads'][0] ) ) {
 | |
| 			edd_add_order_meta( $order_id, 'unlimited_downloads', $meta['_edd_payment_unlimited_downloads'][0] );
 | |
| 		}
 | |
| 
 | |
| 		// Transaction IDs are no longer meta, and have their own table and data set, so we need to add the transactions.
 | |
| 		$transaction_id = ! empty( $meta['_edd_payment_transaction_id'][0] ) ? $meta['_edd_payment_transaction_id'][0] : false;
 | |
| 		// If we have no transaction ID & the gateway was PayPal, let's check in old payment notes.
 | |
| 		if ( empty( $transaction_id ) && false !== strpos( $gateway, 'paypal' ) ) {
 | |
| 			$transaction_id = self::find_transaction_id_from_notes( $order_id );
 | |
| 		}
 | |
| 		if ( ! empty( $transaction_id ) ) {
 | |
| 			edd_add_order_transaction( array(
 | |
| 				'object_id'      => $order_id,
 | |
| 				'object_type'    => 'order',
 | |
| 				'transaction_id' => $transaction_id,
 | |
| 				'gateway'        => $gateway,
 | |
| 				'status'         => 'complete',
 | |
| 				'total'          => $order_total,
 | |
| 				'date_created'   => $date_completed,
 | |
| 				'date_modified'  => $date_completed,
 | |
| 			) );
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * By default, this is what is stored in payment meta. These array keys are part of the core payment meta in 2.x
 | |
| 		 * but are not needed as part of the order meta and will not be migrated.
 | |
| 		 * Extensions can add their keys to this filter if they use the payment meta array to store data and have
 | |
| 		 * established a migration process to keep the data intact with the new order tables.
 | |
| 		 *
 | |
| 		 * @since 3.0
 | |
| 		 * @param array The array of payment meta keys.
 | |
| 		 */
 | |
| 		$core_meta_keys = apply_filters( 'edd_30_payment_meta_keys_not_migrated', array(
 | |
| 			'fees',
 | |
| 			'key',
 | |
| 			'email',
 | |
| 			'date',
 | |
| 			'downloads',
 | |
| 			'cart_details',
 | |
| 			'currency',
 | |
| 			'discount',
 | |
| 			'subtotal',
 | |
| 			'tax',
 | |
| 			'amount',
 | |
| 			'user_id',
 | |
| 		) );
 | |
| 
 | |
| 		// Remove core keys from `user_info`.
 | |
| 		$remaining_user_info = false;
 | |
| 		if ( ! empty( $user_info ) ) {
 | |
| 			/**
 | |
| 			 * Array keys which are part of the core `user_info` in payment meta which are not needed as part of the order meta.
 | |
| 			 * Extensions can add their keys to this filter if they use the `user_info` array to store data and have
 | |
| 			 * established a migration process to keep the data intact with the new order tables.
 | |
| 			 *
 | |
| 			 * @since 3.0
 | |
| 			 * @param array The array of user info keys.
 | |
| 			 */
 | |
| 			$core_user_info      = apply_filters( 'edd_30_core_user_info', array( 'id', 'email', 'first_name', 'last_name', 'discount', 'address', 'user_id' ) );
 | |
| 			$remaining_user_info = array_diff_key( $user_info, array_flip( $core_user_info ) );
 | |
| 		}
 | |
| 
 | |
| 		// If an extension has added data to `user_info`, migrate it.
 | |
| 		if ( $remaining_user_info ) {
 | |
| 			$payment_meta['user_info'] = $remaining_user_info;
 | |
| 		} else {
 | |
| 			$core_meta_keys[] = 'user_info';
 | |
| 		}
 | |
| 
 | |
| 		// Remove all the core payment meta from the array, and...
 | |
| 		if ( is_array( $payment_meta ) ) {
 | |
| 			$remaining_payment_meta = array_diff_key( $payment_meta, array_flip( $core_meta_keys ) );
 | |
| 
 | |
| 			// ..If we have extra payment meta, it needs to be migrated across.
 | |
| 			if ( 0 < count( $remaining_payment_meta ) ) {
 | |
| 				edd_add_order_meta( $order_id, 'payment_meta', $remaining_payment_meta );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/** Create order items ***************************************/
 | |
| 
 | |
| 		// Now we iterate through all the cart items and make rows in the order items table.
 | |
| 		if ( ! empty( $cart_details ) ) {
 | |
| 			foreach ( $cart_details as $key => $cart_item ) {
 | |
| 				// Reset any conditional IDs to be safe.
 | |
| 				$refund_order_item_id = 0;
 | |
| 
 | |
| 				// Get product name.
 | |
| 				$product_name = isset( $cart_item['name'] )
 | |
| 					? $cart_item['name']
 | |
| 					: '';
 | |
| 
 | |
| 				// Get price ID.
 | |
| 				$price_id = self::get_valid_price_id_for_cart_item( $cart_item );
 | |
| 
 | |
| 				if ( ! empty( $product_name ) ) {
 | |
| 					$option_name = edd_get_price_option_name( $cart_item['id'], $price_id );
 | |
| 					if ( ! empty( $option_name ) ) {
 | |
| 						$product_name .= ' — ' . $option_name;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				$order_item_args = array(
 | |
| 					'order_id'      => $order_id,
 | |
| 					'product_id'    => $cart_item['id'],
 | |
| 					'product_name'  => $product_name,
 | |
| 					'price_id'      => $price_id,
 | |
| 					'cart_index'    => $key,
 | |
| 					'type'          => 'download',
 | |
| 					'status'        => $order_status,
 | |
| 					'quantity'      => $cart_item['quantity'],
 | |
| 					'amount'        => (float) $cart_item['item_price'],
 | |
| 					'subtotal'      => (float) $cart_item['subtotal'],
 | |
| 					'discount'      => (float) $cart_item['discount'],
 | |
| 					'tax'           => $cart_item['tax'],
 | |
| 					'total'         => (float) $cart_item['price'],
 | |
| 					'date_created'  => $date_created_gmt,
 | |
| 					'date_modified' => $data->post_modified_gmt,
 | |
| 				);
 | |
| 
 | |
| 				/**
 | |
| 				 * Filters the arguments used to create the order item.
 | |
| 				 *
 | |
| 				 * @since 1.0
 | |
| 				 *
 | |
| 				 * @param array $order_item_args Order item arguments.
 | |
| 				 * @param array $cart_item       Original cart item.
 | |
| 				 * @param array $payment_meta    Payment meta.
 | |
| 				 * @param array $meta            All meta.
 | |
| 				 */
 | |
| 				$order_item_args = apply_filters( 'edd_30_migration_order_item_creation_data', $order_item_args, $cart_item, $payment_meta, $meta );
 | |
| 
 | |
| 				$order_item_id = edd_add_order_item( $order_item_args );
 | |
| 
 | |
| 				if ( ! empty( $cart_item['item_number']['options'] ) ) {
 | |
| 					// Collect any item_number options and store them.
 | |
| 
 | |
| 					// Remove our price_id and quantity, as they are columns on the order item now.
 | |
| 					unset( $cart_item['item_number']['options']['price_id'] );
 | |
| 					unset( $cart_item['item_number']['options']['quantity'] );
 | |
| 
 | |
| 					foreach ( $cart_item['item_number']['options'] as $option_key => $value ) {
 | |
| 						$option_key = '_option_' . sanitize_key( $option_key );
 | |
| 
 | |
| 						edd_add_order_item_meta( $order_item_id, $option_key, $value );
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// If the order status is refunded, we also need to add all the refunded order items on the refund order as well.
 | |
| 				if ( ! empty( $refund_id ) ) {
 | |
| 
 | |
| 					// Since the refund is a near copy of the original order, copy over the arguments.
 | |
| 					$refund_item_args = $order_item_args;
 | |
| 
 | |
| 					$refund_item_args['parent']   = $order_item_id;
 | |
| 					$refund_item_args['order_id'] = $refund_id;
 | |
| 					$refund_item_args['status']   = 'complete';
 | |
| 
 | |
| 					// Subtotal is actually set to subtotal - discount.
 | |
| 					$refund_item_args['subtotal'] = $refund_item_args['subtotal'] - $refund_item_args['discount'];
 | |
| 
 | |
| 					// Negate the amounts
 | |
| 					$refund_item_args['quantity'] = edd_negate_int( $cart_item['quantity'] );
 | |
| 					foreach( array( 'amount', 'subtotal', 'tax', 'total' ) as $field_to_negate ) {
 | |
| 						$refund_item_args[ $field_to_negate ] = edd_negate_amount( $refund_item_args[ $field_to_negate ] );
 | |
| 					}
 | |
| 
 | |
| 					// These are our best estimates since we did not store the refund date previously.
 | |
| 					$refund_item_args['date_crated']   = $data->post_modified_gmt;
 | |
| 					$refund_item_args['date_modified'] = $data->post_modified_gmt;
 | |
| 
 | |
| 					$refund_order_item_id = edd_add_order_item( $refund_item_args );
 | |
| 
 | |
| 					if ( ! empty( $cart_item['item_number']['options'] ) ) {
 | |
| 						// Collect any item_number options and store them.
 | |
| 
 | |
| 						// Remove our price_id and quantity, as they are columns on the order item now.
 | |
| 						unset( $cart_item['item_number']['options']['price_id'] );
 | |
| 						unset( $cart_item['item_number']['options']['quantity'] );
 | |
| 
 | |
| 						foreach ( $cart_item['item_number']['options'] as $option_key => $value ) {
 | |
| 							$option_key = '_option_' . sanitize_key( $option_key );
 | |
| 
 | |
| 							edd_add_order_item_meta( $refund_order_item_id, $option_key, $value );
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 				}
 | |
| 
 | |
| 				// Store order item fees as adjustments.
 | |
| 				if ( isset( $cart_item['fees'] ) && ! empty( $cart_item['fees'] ) ) {
 | |
| 					foreach ( $cart_item['fees'] as $fee_id => $fee ) {
 | |
| 						// Reset any conditional IDs to be safe.
 | |
| 						$refund_adjustment_id = 0;
 | |
| 
 | |
| 						$tax   = EDD()->fees->get_calculated_tax( $fee, $tax_rate );
 | |
| 						$total = floatval( $fee['amount'] ) + $tax;
 | |
| 
 | |
| 						// Track order item fees tax to adjust order if needed.
 | |
| 						$order_items_fees_tax += $tax;
 | |
| 
 | |
| 						// Add the adjustment.
 | |
| 						$adjustment_args = array(
 | |
| 							'object_id'   => $order_item_id,
 | |
| 							'object_type' => 'order_item',
 | |
| 							'type_key'    => $fee_id,
 | |
| 							'type'        => 'fee',
 | |
| 							'description' => $fee['label'],
 | |
| 							'subtotal'    => floatval( $fee['amount'] ),
 | |
| 							'tax'         => $tax,
 | |
| 							'total'       => floatval( $fee['amount'] ) + $tax,
 | |
| 						);
 | |
| 
 | |
| 						/**
 | |
| 						 * Filters the arguments used to create an order item adjustment.
 | |
| 						 *
 | |
| 						 * @since 3.0
 | |
| 						 *
 | |
| 						 * @param array $adjustment_args Adjustment arguments for a fee.
 | |
| 						 * @param array $fee             Original fee data.
 | |
| 						 * @param array $cart_item       Cart item this fee is part of.
 | |
| 						 * @param array $payment_meta    Payment meta.
 | |
| 						 * @param array $meta            All meta.
 | |
| 						 */
 | |
| 						$adjustment_args = apply_filters( 'edd_30_migration_order_item_adjustment_creation_data', $adjustment_args, $fee, $cart_item, $payment_meta, $meta );
 | |
| 
 | |
| 						$adjustment_id = edd_add_order_adjustment( $adjustment_args );
 | |
| 
 | |
| 						// If we refunded the main order, the fees also need to be added to the refund order type we created.
 | |
| 						if ( ! empty( $refund_id ) ) {
 | |
| 							$refund_adjustment_args              = $adjustment_args;
 | |
| 							$refund_adjustment_args['parent']    = $adjustment_id;
 | |
| 							$refund_adjustment_args['object_id'] = $refund_order_item_id;
 | |
| 							$refund_adjustment_args['subtotal']  = edd_negate_amount( floatval( $fee['amount'] ) );
 | |
| 							$refund_adjustment_args['tax']       = edd_negate_amount( $tax );
 | |
| 							$refund_adjustment_args['total']     = edd_negate_amount( floatval( $fee['amount'] ) + $tax );
 | |
| 
 | |
| 							$refund_adjustment_id = edd_add_order_adjustment( $refund_adjustment_args );
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// Compatibility with older versions of EDD.
 | |
| 			// Older versions stored a single dimensional array of download IDs.
 | |
| 		} elseif ( is_array( $cart_downloads ) && count( $cart_downloads ) === count( $cart_downloads, COUNT_RECURSIVE ) ) {
 | |
| 			foreach ( $cart_downloads as $cart_index => $download_id ) {
 | |
| 				$download = edd_get_download( $download_id );
 | |
| 
 | |
| 				$order_item_args = array(
 | |
| 					'order_id'      => $order_id,
 | |
| 					'product_id'    => $download_id,
 | |
| 					'product_name'  => $download->post_name,
 | |
| 					'price_id'      => null,
 | |
| 					'cart_index'    => $cart_index,
 | |
| 					'type'          => 'download',
 | |
| 					'quantity'      => 1,
 | |
| 					'amount'        => (float) $payment_meta['amount'],
 | |
| 					'subtotal'      => (float) $payment_meta['amount'],
 | |
| 					'discount'      => 0.00,
 | |
| 					'tax'           => 0.00,
 | |
| 					'total'         => (float) $payment_meta['amount'],
 | |
| 					'date_created'  => $date_created_gmt,
 | |
| 					'date_modified' => $data->post_modified_gmt,
 | |
| 				);
 | |
| 
 | |
| 				$order_item_id = edd_add_order_item( $order_item_args );
 | |
| 
 | |
| 				// If the order was refunded, we also need to add these items to the refund order.
 | |
| 				if ( ! empty( $refund_id ) ) {
 | |
| 
 | |
| 					// Since the refund is a near copy of the original order, copy over the arguments.
 | |
| 					$refund_item_args = $order_item_args;
 | |
| 
 | |
| 					$refund_item_args['parent']   = $order_item_id;
 | |
| 					$refund_item_args['order_id'] = $refund_id;
 | |
| 					$refund_item_args['quantity'] = edd_negate_int( 1 );
 | |
| 					$refund_item_args['amount']   = edd_negate_amount( (float) $payment_meta['amount'] );
 | |
| 					$refund_item_args['subtotal'] = edd_negate_amount( (float) $payment_meta['amount'] );
 | |
| 					$refund_item_args['total']    = edd_negate_amount( (float) $payment_meta['amount'] );
 | |
| 
 | |
| 					// These are the best guess at the time, since we didn't store this data previously.
 | |
| 					$refund_item_args['date_created']  = $data->post_modified_gmt;
 | |
| 					$refund_item_args['date_modified'] = $data->post_modified_gmt;
 | |
| 
 | |
| 					edd_add_order_item( $order_item_args );
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/** Create order adjustments *********************************/
 | |
| 
 | |
| 		if ( isset( $payment_meta['fees'] ) && ! empty( $payment_meta['fees'] ) ) {
 | |
| 			foreach ( $payment_meta['fees'] as $fee_id => $fee ) {
 | |
| 				// Reset any conditional IDs to be safe.
 | |
| 				$refund_adjustment_id = 0;
 | |
| 
 | |
| 				if ( ! empty( $fee['download_id'] ) ) {
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				$tax   = EDD()->fees->get_calculated_tax( $fee, $tax_rate );
 | |
| 				$total = floatval( $fee['amount'] ) + $tax;
 | |
| 
 | |
| 				$order_fees_tax   += $tax;
 | |
| 				$order_fees_total += $total;
 | |
| 
 | |
| 				// Add the adjustment.
 | |
| 				$adjustment_args = array(
 | |
| 					'object_id'     => $order_id,
 | |
| 					'object_type'   => 'order',
 | |
| 					'type_key'      => $fee_id,
 | |
| 					'type'          => 'fee',
 | |
| 					'description'   => $fee['label'],
 | |
| 					'subtotal'      => floatval( $fee['amount'] ),
 | |
| 					'tax'           => $tax,
 | |
| 					'total'         => $total,
 | |
| 					'date_created'  => $date_created_gmt,
 | |
| 					'date_modified' => $data->post_modified_gmt,
 | |
| 				);
 | |
| 
 | |
| 				/**
 | |
| 				 * Filters the order adjustment arguments.
 | |
| 				 *
 | |
| 				 * @since 3.0
 | |
| 				 *
 | |
| 				 * @param array $adjustment_args Arguments used to create the order adjustment.
 | |
| 				 * @param array $fee             Fee data.
 | |
| 				 * @param array $payment_meta    Payment meta.
 | |
| 				 * @param array $meta            All meta.
 | |
| 				 */
 | |
| 				$adjustment_args = apply_filters( 'edd_30_migration_order_adjustment_creation_data', $adjustment_args, $fee, $payment_meta, $meta );
 | |
| 
 | |
| 				$adjustment_id = edd_add_order_adjustment( $adjustment_args );
 | |
| 
 | |
| 				if ( ! empty( $refund_id ) ) {
 | |
| 
 | |
| 					// Since the refund is a near copy of the original order, copy over the arguments.
 | |
| 					$refund_adjustment_args = $adjustment_args;
 | |
| 
 | |
| 					$refund_adjustment_args['parent']    = $adjustment_id;
 | |
| 					$refund_adjustment_args['object_id'] = $refund_id;
 | |
| 
 | |
| 					// Negate the amounts.
 | |
| 					$refund_adjustment_args['subtotal'] = edd_negate_amount( floatval( $fee['amount'] ) );
 | |
| 					$refund_adjustment_args['tax']      = edd_negate_amount( $tax );
 | |
| 					$refund_adjustment_args['total']    = edd_negate_amount( floatval( $fee['amount'] ) + $tax );
 | |
| 
 | |
| 					$refund_adjustment_id = edd_add_order_adjustment( $refund_adjustment_args );
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Add fee taxes (order and order item) if the order tax amount was previously manually calculated.
 | |
| 		if ( false === $meta_tax ) {
 | |
| 			edd_update_order( $order_id, array(
 | |
| 				'tax' => $order_tax + $order_fees_tax + $order_items_fees_tax,
 | |
| 			) );
 | |
| 		}
 | |
| 
 | |
| 		// Add fee totals (order and order item) if the order tax amount was previously manually calculated.
 | |
| 		// Order item fees were previously included in the total calculation. We must manually include
 | |
| 		// order item fee tax amounts, and order fees total (subtotal + tax).
 | |
| 		if ( false === $meta_total ) {
 | |
| 			edd_update_order( $order_id, array(
 | |
| 				'total' => $order_total + $order_fees_total + $order_items_fees_tax,
 | |
| 			) );
 | |
| 		}
 | |
| 
 | |
| 		// Insert discounts.
 | |
| 		$discounts = ! empty( $user_info['discount'] )
 | |
| 			? $user_info['discount']
 | |
| 			: array();
 | |
| 
 | |
| 		if ( ! is_array( $discounts ) ) {
 | |
| 			$discounts = explode( ',', $discounts );
 | |
| 		}
 | |
| 
 | |
| 		if ( ! empty( $discounts ) && ( 'none' !== $discounts[0] ) ) {
 | |
| 			if ( 1 === count( $discounts ) ) {
 | |
| 				$discount_code = reset( $discounts );
 | |
| 
 | |
| 				/** @var \EDD_Discount $discount_object */
 | |
| 				$discount_object = edd_get_discount_by( 'code', $discount_code );
 | |
| 
 | |
| 				if ( $discount_object instanceof \EDD_Discount ) {
 | |
| 					$discount_args = array(
 | |
| 						'object_id'     => $order_id,
 | |
| 						'object_type'   => 'order',
 | |
| 						'type_id'       => $discount_object->id,
 | |
| 						'type'          => 'discount',
 | |
| 						'description'   => $discount_object->code,
 | |
| 						'subtotal'      => $order_discount,
 | |
| 						'total'         => $order_discount,
 | |
| 						'date_created'  => $date_created_gmt,
 | |
| 						'date_modified' => $data->post_modified_gmt,
 | |
| 					);
 | |
| 
 | |
| 					/**
 | |
| 					 * Filters the arguments used to create a discount adjustment.
 | |
| 					 *
 | |
| 					 * @since 3.0
 | |
| 					 *
 | |
| 					 * @param array         $discount_args   Order adjustment arguments.
 | |
| 					 * @param \EDD_Discount $discount_object Discount object.
 | |
| 					 * @param float         $order_subtotal  Order subtotal.
 | |
| 					 * @param array         $user_info       User info array.
 | |
| 					 * @param array         $payment_meta    Payment meta.
 | |
| 					 * @param array         $meta            All post meta.
 | |
| 					 */
 | |
| 					$discount_args = apply_filters( 'edd_30_migration_order_discount_creation_data', $discount_args, $discount_object, $order_subtotal, $user_info, $payment_meta, $meta );
 | |
| 
 | |
| 					$new_discount_id = edd_add_order_adjustment( $discount_args );
 | |
| 					if ( $order_discount <= 0 ) {
 | |
| 						edd_add_order_adjustment_meta(
 | |
| 							$new_discount_id,
 | |
| 							'migrated_order_discount_unknown',
 | |
| 							(int) $order_id,
 | |
| 							true
 | |
| 						);
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				foreach ( $discounts as $discount_code ) {
 | |
| 
 | |
| 					/** @var \EDD_Discount $discount_object */
 | |
| 					$discount_object = edd_get_discount_by( 'code', $discount_code );
 | |
| 
 | |
| 					if ( false === $discount_object ) {
 | |
| 						continue;
 | |
| 					}
 | |
| 
 | |
| 					$calculated_discount = $order_subtotal - $discount_object->get_discounted_amount( $order_subtotal );
 | |
| 					$discount_args       = array(
 | |
| 						'object_id'     => $order_id,
 | |
| 						'object_type'   => 'order',
 | |
| 						'type_id'       => $discount_object->id,
 | |
| 						'type'          => 'discount',
 | |
| 						'description'   => $discount_object->code,
 | |
| 						'subtotal'      => $calculated_discount,
 | |
| 						'total'         => $calculated_discount,
 | |
| 						'date_created'  => $date_created_gmt,
 | |
| 						'date_modified' => $data->post_modified_gmt,
 | |
| 					);
 | |
| 
 | |
| 					/**
 | |
| 					 * Filters the arguments used to create a discount adjustment.
 | |
| 					 *
 | |
| 					 * @since 3.0
 | |
| 					 *
 | |
| 					 * @param array         $discount_args   Order adjustment arguments.
 | |
| 					 * @param \EDD_Discount $discount_object Discount object.
 | |
| 					 * @param float         $order_subtotal  Order subtotal.
 | |
| 					 * @param array         $user_info       User info array.
 | |
| 					 * @param array         $payment_meta    Payment meta.
 | |
| 					 * @param array         $meta            All post meta.
 | |
| 					 */
 | |
| 					$discount_args = apply_filters( 'edd_30_migration_order_discount_creation_data', $discount_args, $discount_object, $order_subtotal, $user_info, $payment_meta, $meta );
 | |
| 
 | |
| 					$new_discount_id = edd_add_order_adjustment( $discount_args );
 | |
| 					if ( $calculated_discount <= 0 ) {
 | |
| 						edd_add_order_adjustment_meta(
 | |
| 							$new_discount_id,
 | |
| 							'migrated_order_discount_unknown',
 | |
| 							(int) $order_id,
 | |
| 							true
 | |
| 						);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/** Create order meta ****************************************/
 | |
| 
 | |
| 		$core_meta_keys = array(
 | |
| 			'_edd_payment_user_email',
 | |
| 			'_edd_payment_customer_id',
 | |
| 			'_edd_payment_user_id',
 | |
| 			'_edd_payment_user_ip',
 | |
| 			'_edd_payment_purchase_key',
 | |
| 			'_edd_payment_total',
 | |
| 			'_edd_payment_mode',
 | |
| 			'_edd_payment_gateway',
 | |
| 			'_edd_payment_meta',
 | |
| 			'_edd_payment_tax',
 | |
| 			'_edd_payment_tax_rate',
 | |
| 			'_edd_completed_date',
 | |
| 			'_edd_payment_unlimited_downloads',
 | |
| 			'_edd_payment_number',
 | |
| 			'_edd_payment_transaction_id',
 | |
| 		);
 | |
| 
 | |
| 		// Determine what main payment meta keys were from core and what were custom...
 | |
| 		$remaining_meta = array_diff_key( $meta, array_flip( $core_meta_keys ) );
 | |
| 
 | |
| 		// ...and whatever is not from core, needs to be added as new order meta.
 | |
| 		foreach ( $remaining_meta as $meta_key => $meta_value ) {
 | |
| 			$meta_value = maybe_unserialize( $meta_value[0] );
 | |
| 
 | |
| 			edd_add_order_meta( $order_id, $meta_key, $meta_value );
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * Now that we're done, let's run a hook here so we can allow extensions to make any necessary changes.
 | |
| 		 *
 | |
| 		 * @since 3.0
 | |
| 		 * @param int   $order_id     The order ID.
 | |
| 		 * @param array $payment_meta The `_edd_payment_meta` value for the original payment.
 | |
| 		 * @param array $meta         All post meta associated with the payment.
 | |
| 		 */
 | |
| 		do_action( 'edd_30_migrate_order', $order_id, $payment_meta, $meta );
 | |
| 
 | |
| 		return $order_id;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Retrieves a valid price ID for a given cart item.
 | |
| 	 * If the product does not have variable prices, then `null` is always returned.
 | |
| 	 * If the supplied price ID does not match a price ID that actually exists, then the default
 | |
| 	 * variable price is returned instead of the supplied one.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param array $cart_item Array of cart item details.
 | |
| 	 *
 | |
| 	 * @return int|null
 | |
| 	 */
 | |
| 	protected static function get_valid_price_id_for_cart_item( $cart_item ) {
 | |
| 		// If the product doesn't have variable prices, just return `null`.
 | |
| 		if ( ! edd_has_variable_prices( $cart_item['id'] ) ) {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		$variable_prices = edd_get_variable_prices( $cart_item['id'] );
 | |
| 		if ( ! is_array( $variable_prices ) || empty( $variable_prices ) ) {
 | |
| 			return null;
 | |
| 		}
 | |
| 
 | |
| 		// Return the price ID that's set to the cart item right now, if not numeric return NULL.
 | |
| 		return isset( $cart_item['item_number']['options']['price_id'] ) && is_numeric( $cart_item['item_number']['options']['price_id'] )
 | |
| 			? absint( $cart_item['item_number']['options']['price_id'] )
 | |
| 			: null;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Attempts to locate a PayPal transaction ID from legacy payment notes.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param int $payment_id
 | |
| 	 *
 | |
| 	 * @return string|false Transaction ID on success, false if not found.
 | |
| 	 */
 | |
| 	private static function find_transaction_id_from_notes( $payment_id ) {
 | |
| 		global $wpdb;
 | |
| 
 | |
| 		$payment_notes = $wpdb->get_col( $wpdb->prepare(
 | |
| 			"SELECT comment_content FROM {$wpdb->comments} WHERE comment_post_ID = %d",
 | |
| 			$payment_id
 | |
| 		) );
 | |
| 
 | |
| 		if ( empty( $payment_notes ) || ! is_array( $payment_notes ) ) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		foreach ( $payment_notes as $note ) {
 | |
| 			if ( preg_match( '/^PayPal Transaction ID: ([^\s]+)/', $note, $match ) ) {
 | |
| 				return $match[1];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Tax rates.
 | |
| 	 *
 | |
| 	 * @since 3.0
 | |
| 	 *
 | |
| 	 * @param object $data Data to migrate.
 | |
| 	 */
 | |
| 	public static function tax_rates( $data = null ) {
 | |
| 
 | |
| 		// Bail if no data passed.
 | |
| 		if ( ! $data ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$scope = ! empty( $data['global'] )
 | |
| 			? 'country'
 | |
| 			: 'region';
 | |
| 
 | |
| 		// If the scope is 'country', look for other active rates that are country wide and set them as 'inactive'.
 | |
| 		if ( 'country' === $scope ) {
 | |
| 			$tax_rates = edd_get_adjustments(
 | |
| 				array(
 | |
| 					'type'   => 'tax_rate',
 | |
| 					'status' => 'active',
 | |
| 					'scope'  => 'country',
 | |
| 					'name'   => $data['country'],
 | |
| 				)
 | |
| 			);
 | |
| 
 | |
| 			if ( ! empty( $tax_rates ) ) {
 | |
| 				foreach ( $tax_rates as $tax_rate ) {
 | |
| 					edd_update_adjustment(
 | |
| 						$tax_rate->id,
 | |
| 						array( 'status' => 'inactive', )
 | |
| 					);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$adjustment_data = array(
 | |
| 			'name'   => $data['country'],
 | |
| 			'scope'  => $scope,
 | |
| 			'amount' => floatval( $data['rate'] ),
 | |
| 		);
 | |
| 
 | |
| 		if ( ! empty( $data['state'] ) ) {
 | |
| 			$adjustment_data['description'] = sanitize_text_field( $data['state'] );
 | |
| 		}
 | |
| 
 | |
| 		edd_add_tax_rate( $adjustment_data );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Normalizes and backfills legacy payment cart data.
 | |
| 	 *
 | |
| 	 * @since 3.0.0
 | |
| 	 *
 | |
| 	 * @param array|string $cart_details Cart details. No action is performed if a string
 | |
| 	 *                                   (array cannot be unserialized) is provided.
 | |
| 	 * @return array|string
 | |
| 	 */
 | |
| 	private static function normalize_cart_details( $cart_details ) {
 | |
| 		if ( ! is_array( $cart_details ) ) {
 | |
| 			return $cart_details;
 | |
| 		}
 | |
| 
 | |
| 		foreach ( $cart_details as &$cart_item ) {
 | |
| 
 | |
| 			// Get price.
 | |
| 			$cart_item['price'] = isset( $cart_item['price'] )
 | |
| 				? (float) $cart_item['price']
 | |
| 				: 0.00;
 | |
| 
 | |
| 			// Get item price.
 | |
| 			$cart_item['item_price'] = isset( $cart_item['item_price'] )
 | |
| 				? (float) $cart_item['item_price']
 | |
| 				: (float) $cart_item['price'];
 | |
| 
 | |
| 			// Get quantity.
 | |
| 			$cart_item['quantity'] = isset( $cart_item['quantity'] )
 | |
| 				? $cart_item['quantity']
 | |
| 				: 1;
 | |
| 
 | |
| 			// Get subtotal.
 | |
| 			$cart_item['subtotal'] = isset( $cart_item['subtotal'] )
 | |
| 				? (float) $cart_item['subtotal']
 | |
| 				: (float) $cart_item['quantity'] * $cart_item['item_price'];
 | |
| 
 | |
| 			// Get discount.
 | |
| 			$cart_item['discount'] = isset( $cart_item['discount'] )
 | |
| 				? (float) $cart_item['discount']
 | |
| 				: 0.00;
 | |
| 
 | |
| 			// Get tax.
 | |
| 			$cart_item['tax'] = isset( $cart_item['tax'] )
 | |
| 				? (float) $cart_item['tax']
 | |
| 				: 0.00;
 | |
| 		}
 | |
| 
 | |
| 		return $cart_details;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Given that some data quite possible has bad serialization, we need to possibly fix the bad serialization.
 | |
| 	 *
 | |
| 	 * @since 3.0.0
 | |
| 	 *
 | |
| 	 * @param $data
 | |
| 	 *
 | |
| 	 * @return mixed
 | |
| 	 */
 | |
| 	public static function fix_possible_serialization( $data ) {
 | |
| 		if ( ! is_array( $data ) && is_string( $data ) ) {
 | |
| 			$data = substr_replace( $data, 'a', 0, 1 );
 | |
| 		}
 | |
| 
 | |
| 		return $data;
 | |
| 	}
 | |
| }
 |