From 4c1abfe6c0c3f2b664b86ab55f206e1520b491fc Mon Sep 17 00:00:00 2001 From: Vova Feldman Date: Mon, 31 Oct 2016 12:22:13 -0400 Subject: [PATCH 01/22] Added WooCommerce classes (replicated from EDD). --- config.php | 4 + .../wc/class-fs-wc-download-migration.php | 388 ++++++ .../wc/class-fs-wc-migration-endpoint.php | 345 +++++ .../migration/wc/class-fs-wc-migration.php | 1118 +++++++++++++++++ 4 files changed, 1855 insertions(+) create mode 100644 includes/migration/wc/class-fs-wc-download-migration.php create mode 100644 includes/migration/wc/class-fs-wc-migration-endpoint.php create mode 100644 includes/migration/wc/class-fs-wc-migration.php diff --git a/config.php b/config.php index b2eced1..b512bd7 100644 --- a/config.php +++ b/config.php @@ -26,6 +26,10 @@ define( 'WP_FS__NAMESPACE_EDD', 'EDD' ); } + if ( ! defined( 'WP_FS__NAMESPACE_WC' ) ) { + define( 'WP_FS__NAMESPACE_WC', 'WC' ); + } + if ( ! defined( 'WP_FS__IS_PRODUCTION_MODE' ) ) { // By default, run with Freemius production servers. define( 'WP_FS__IS_PRODUCTION_MODE', true ); diff --git a/includes/migration/wc/class-fs-wc-download-migration.php b/includes/migration/wc/class-fs-wc-download-migration.php new file mode 100644 index 0000000..bdede6c --- /dev/null +++ b/includes/migration/wc/class-fs-wc-download-migration.php @@ -0,0 +1,388 @@ +> + */ + private $_edd_prices = array(); + + /** + * @var int + */ + private $_edd_free_price_id; + + /** + * @var bool + */ + private $_edd_has_paid_plan = false; + + /** + * @var array + */ + private $_edd_paid_plan_pricing = array(); + + #-------------------------------------------------------------------------------- + #region Init + #-------------------------------------------------------------------------------- + + function __construct( + FS_Developer $developer, + $module, + EDD_Download $download + ) { + $this->init( WP_FS__NAMESPACE_WC, $developer, $module ); + + $this->_edd_download = $download; + + if ( $download->has_variable_prices() ) { + $this->_edd_prices = $download->get_prices(); + } else { + if ( class_exists( 'EDD_Recurring' ) ) { + $recurring = EDD_Recurring::is_recurring( $download->ID ); + $period = EDD_Recurring::get_period_single( $download->ID ); + } else { + $recurring = false; + $period = 'never'; + } + + $this->_edd_prices = array( + // Set the EDD price ID as ZERO when the download doesn't have variable prices. + 0 => array( + 'recurring' => $recurring ? 'yes' : 'no', + 'period' => $period, + 'license_limit' => 0, + 'amount' => $download->get_price(), + ) + ); + } + + $this->process_local_pricing(); + } + + #endregion + + #-------------------------------------------------------------------------------- + #region Helper Methods + #-------------------------------------------------------------------------------- + + /** + * Get billing cycle out of the variable price. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @param array $edd_price + * + * @return string + */ + private function get_billing_cycle( $edd_price ) { + $billing_cycle = 'lifetime'; + + if ( ! empty( $edd_price['recurring'] ) && + 'yes' === $edd_price['recurring'] && + ! empty( $edd_price['period'] ) && + 'never' !== $edd_price['period'] + ) { + switch ( $edd_price['period'] ) { + case 'year': + $billing_cycle = 'annual'; + break; + case 'month': + $billing_cycle = 'monthly'; + break; + default: + // @todo Throw an error when billing cycle is not supported. + $billing_cycle = 'monthly'; + break; + } + } + + return $billing_cycle; + } + + /** + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @param number $id + * + * @return string + */ + private function get_edd_unique_price_id( $id ) { + return $this->_edd_download->ID . ':' . $id; + } + + /** + * Aggregate EDD prices based on license limits. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + */ + private function process_local_pricing() { + $edd_paid_plan_pricing_by_licenses = array(); + + foreach ( $this->_edd_prices as $id => &$edd_price ) { + $licenses = (int) $edd_price['license_limit']; + + // Add price ID to data. + $edd_price['_id'] = $id; + + // Check if free plan. + $amount = floatval( $edd_price['amount'] ); + + if ( .0 >= $amount ) { + $this->_edd_free_price_id = $this->get_edd_unique_price_id( $id ); + continue; + } + + if ( ! isset( $edd_paid_plan_pricing_by_licenses[ $licenses ] ) ) { + $edd_paid_plan_pricing_by_licenses[ $licenses ] = array(); + } + + // Paid plan. + $edd_paid_plan_pricing_by_licenses[ $licenses ][] = $edd_price; + + $this->_edd_has_paid_plan = true; + } + + foreach ( $edd_paid_plan_pricing_by_licenses as $licenses => $edd_prices ) { + $pricing = array( + 'edd_prices_ids' => array() + ); + + $pricing['licenses'] = ( $licenses > 0 ) ? $licenses : null; + + foreach ( $edd_prices as $edd_price ) { + $amount = floatval( $edd_price['amount'] ); + + $billing_cycle = $this->get_billing_cycle( $edd_price ); + $pricing["{$billing_cycle}_price"] = $amount; + + // We need to store EDD price IDs list to link them with the pricing. + $pricing['edd_prices_ids'][] = $this->get_edd_unique_price_id( $edd_price['_id'] ); + } + + $this->_edd_paid_plan_pricing[] = $pricing; + } + } + + #endregion + + #-------------------------------------------------------------------------------- + #region EDD Required Data Getters + #-------------------------------------------------------------------------------- + + /** + * Get local module ID. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @return bool + */ + protected function get_local_module_id() { + return $this->_edd_download->ID; + } + + /** + * Check if download has a free plan. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @return bool + */ + protected function local_has_free_plan() { + return isset( $this->_edd_free_price_id ); + } + + /** + * Get free price ID as the plan ID since EDD doesn't have a concept of plans. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @return bool + */ + protected function get_local_free_plan_id() { + return $this->_edd_download->ID . ':' . $this->_edd_free_price_id . 'free'; + } + + /** + * Check download has any price that is not free. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @return bool + */ + protected function local_has_paid_plan() { + return $this->_edd_has_paid_plan; + } + + /** + * Return all the prices IDs that are paid and belong to the + * same plan (identical feature-set). + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @param int $index + * + * @return string[] + */ + protected function get_local_paid_plan_pricing_ids( $index ) { + return $this->_edd_paid_plan_pricing[ $index ]['edd_prices_ids']; + } + + /** + * Get paid plan associated pricing objects count. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @return bool + */ + protected function get_local_paid_plan_pricing_count() { + return count( $this->_edd_paid_plan_pricing ); + } + + /** + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @param int|null $licenses + * + * @return int|false + */ + protected function get_local_paid_plan_pricing_index_by_licenses( $licenses ) { + $index = 0; + + foreach ( $this->_edd_paid_plan_pricing as $edd_pricing ) { + if ( $licenses === $edd_pricing['licenses'] ) { + return $index; + } + + $index ++; + } + + return false; + } + + /** + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @param int $index + * + * @return int|null + */ + protected function get_local_paid_plan_pricing_licenses( $index ) { + return $this->_edd_paid_plan_pricing[ $index ]['licenses']; + } + + #endregion + + #-------------------------------------------------------------------------------- + #region Data Mapping for API + #-------------------------------------------------------------------------------- + + /** + * Generate module details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_module_for_api() { + $download_post = WP_Post::get_instance( $this->_edd_download->get_ID() ); + + return array( + 'slug' => $download_post->post_name, + 'title' => $this->_edd_download->get_name(), + 'type' => 'plugin', + 'business_model' => $this->get_local_business_model(), + 'created_at' => $download_post->post_date_gmt, + ); + } + + /** + * Generate free plan details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_free_plan_for_api() { + return array( + 'name' => 'free', + 'title' => 'Free', + ); + } + + /** + * Generate paid plan details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_paid_plan_for_api() { + return array( + 'name' => 'pro', + 'title' => 'Pro', + // By default create non-blocking plan. + 'is_block_features' => false, + 'is_https_support' => true, + // By default allow localhost activations. + 'is_free_localhost' => true, + // Set paid plan as featured. + 'is_featured' => true, + ); + } + + /** + * Generate paid plan pricing details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param int $index + * + * @return array + */ + protected function get_paid_plan_pricing_for_api( $index ) { + // Clone. + $pricing = $this->_edd_paid_plan_pricing[ $index ]; + + unset( $pricing['edd_prices_ids'] ); + + return $pricing; + } + + #endregion + } \ No newline at end of file diff --git a/includes/migration/wc/class-fs-wc-migration-endpoint.php b/includes/migration/wc/class-fs-wc-migration-endpoint.php new file mode 100644 index 0000000..9c7a586 --- /dev/null +++ b/includes/migration/wc/class-fs-wc-migration-endpoint.php @@ -0,0 +1,345 @@ +init( WP_FS__NAMESPACE_WC ); + + if ( false && ! defined( 'DOING_AJAX' ) ) { + $this->test_full_migration(); + } + } + + #-------------------------------------------------------------------------------- + #region Testing + #-------------------------------------------------------------------------------- + + /** + * Migrate license by ID. + * + * Note: This method is only for testing reasons. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @param int $license_id + * + * @throws Exception + */ + function migrate_license_by_id( $license_id ) { + $namespace = strtolower($this->_namespace); + + require_once WP_FSM__DIR_MIGRATION . '/class-fs-migration-abstract.php'; + require_once WP_FSM__DIR_MIGRATION . "/{$namespace}/class-fs-{$namespace}-migration.php"; + + $migration = FS_WC_Migration::instance( $license_id ); + $migration->set_api( $this->get_api() ); + $migration->do_migrate_license(); + } + + /** + * Test full install's license migration. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + */ + private function test_full_migration() { + $url = 'http://test9.freemius.com'; + $download_id = 25; + $license_key = '74062bc8b9cc256823f8f08d0f8feedf'; + + $params = array( + 'license_key' => $license_key, + 'module_id' => $download_id, + 'url' => $url, + 'site_url' => $url, + 'plugin_version' => '1.2.1', + 'site_uid' => $this->get_anonymous_id( $url ), + 'site_name' => 'Freemius Test', + 'platform_version' => get_bloginfo( 'version' ), + 'php_version' => phpversion(), + 'language' => get_bloginfo( 'language' ), + 'charset' => get_bloginfo( 'charset' ), + 'is_premium' => true, + 'is_active' => true, + 'is_uninstalled' => false, + ); + + $this->maybe_process_api_request( $params ); + } + + #endregion + + #-------------------------------------------------------------------------------- + #region Local Data Getters + #-------------------------------------------------------------------------------- + + /** + * Map WC download into FS object. + * + * @author Vova Feldman (@svovaf) + * @since 1.0.0 + * + * @param EDD_Download $local_module + * + * @return FS_Plugin + */ + protected function local_to_remote_module( $local_module ) { + $module = new FS_Plugin(); + $module->id = $local_module->get_ID(); + $module->slug = $local_module->post_name; + $module->title = $local_module->get_name(); + + return $module; + } + + /** + * Get all WC downloads. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_all_local_modules() { + /** + * @var WP_Post[] $downloads + */ + $downloads = get_posts( array( + 'post_type' => 'download', + 'posts_per_page' => - 1, + ) ); + + for ( $i = 0, $len = count( $downloads ); $i < $len; $i ++ ) { + $downloads[ $i ] = new EDD_Download( $downloads[ $i ]->ID ); + } + + return $downloads; + } + + /** + * Load WC download by ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param string $local_module_id + * + * @return false|\EDD_Download + */ + protected function get_local_module_by_id( $local_module_id ) { + $download_post = WP_Post::get_instance( $local_module_id ); + + return ( empty( $download_post ) || 'download' !== $download_post->post_type ) ? + false : + new EDD_Download( $local_module_id ); + } + + /** + * WC Download slug (post_name). + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param mixed $local_module + * + * @return string + */ + protected function get_local_module_slug( $local_module ) { + return $local_module->post_name; + } + + /** + * Return the instance of the WC download migration manager. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param mixed $local_module + * @param FS_Plugin $module + * + * @return \FS_EDD_Download_Migration + */ + protected function get_local_module_migration_manager( $local_module, FS_Plugin $module = null ) { + require_once WP_FSM__DIR_MIGRATION . '/edd/class-fs-edd-download-migration.php'; + + if ( is_null( $module ) ) { + $module = $this->get_module_by_slug( $this->get_local_module_slug( $local_module ) ); + } + + return new FS_EDD_Download_Migration( + $this->get_developer(), + $module, + $local_module + ); + } + + #endregion + + /** + * Migrate install's license and return FS account's data (user + install). + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + * + * @throws FS_Endpoint_Exception + */ + protected function migrate_install_license() { + require_once WP_FSM__DIR_MIGRATION . '/class-fs-migration-abstract.php'; + require_once WP_FSM__DIR_MIGRATION . '/edd/class-fs-edd-migration.php'; + + $license_id = edd_software_licensing()->get_license_by_key( $this->get_param( 'license_key' ) ); + + $migration = FS_WC_Migration::instance( $license_id ); + $migration->set_api( $this->get_api() ); + + // Migrate customer, purchase/subscription, billing and license. + $customer = $migration->do_migrate_license(); + + // Migrate plugin installation. + return $migration->do_migrate_install( $this->_request_data, $customer ); + } + + #-------------------------------------------------------------------------------- + #region API Request Params Validation + #-------------------------------------------------------------------------------- + + /** + * Validate EDD download license parameters. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @throws FS_Endpoint_Exception + */ + protected function validate_params() { + // Require download ID. + $this->require_unsigned_int( 'module_id' ); + + $download_id = $this->get_param( 'module_id' ); + $license_key = $this->get_param( 'license_key' ); + $url = $this->get_param( 'url' ); + + // Before checking license with EDD, make sure module is synced. + if (false === $this->get_remote_module_id( $download_id )){ + throw new FS_Endpoint_Exception( "Invalid download ID ({$download_id}).", 'invalid_download_id', 400 ); + } + + // Get EDD license state. + $edd_license_state = edd_software_licensing()->check_license( array( + 'item_id' => $download_id, + 'item_name' => '', + 'key' => $license_key, + 'url' => $url, + ) ); + + switch ( $edd_license_state ) { + case 'invalid': + // Invalid license key. + throw new FS_Endpoint_Exception( "Invalid license key ({$license_key}).", 'invalid_license_key', + 400 ); + case 'invalid_item_id': + // Invalid download ID. + throw new FS_Endpoint_Exception( "Invalid download ID ({$download_id}).", 'invalid_download_id', + 400 ); + /** + * Migrate expired license since all EDD licenses are not blocking. + */ + case 'expired': + /** + * License not yet activated. + * + * This use-case should not happen since if the client triggered a migration + * request with a valid license key, it means that the license was activated + * at least once. Hence, 'inactive' isn't possible. + */ + case 'inactive': + /** + * License was disabled, therefore, ... + * + * @todo what to do in that case? + */ + case 'disabled': + /** + * Migrate license & site. + * + * Based on the EDD SL logic this result is trigger when it's a production + * site (not localhost), and the site license wasn't activated. + * + * It can happen for example when the user trying to activate a license + * that is already fully utilized. + * + * @todo what to do in that case? + */ + case 'site_inactive': + /** + * Migrate license & site. + * + * License is valid and activated for the context site. + */ + case 'valid': + break; + case 'item_name_mismatch': + /** + * This use case should never happen since we check the license state + * based on the EDD download ID, not the name. + */ + break; + default: + // Unexpected license state. This case should never happen. + throw new FS_Endpoint_Exception( 'Unexpected EDD download license state.' ); + break; + } + } + + #endregion + } + + /** + * The main function responsible for returning the FS_WC_Migration_Endpoint + * instance. + * + * Example: + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return \FS_WC_Migration_Endpoint The one true FS_WC_Migration_Endpoint Instance + */ + function fs_wc_migration_manager() { + return FS_WC_Migration_Endpoint::instance(); + } + + // Get Freemius EDD Migration running. + fs_wc_migration_manager(); \ No newline at end of file diff --git a/includes/migration/wc/class-fs-wc-migration.php b/includes/migration/wc/class-fs-wc-migration.php new file mode 100644 index 0000000..090ab74 --- /dev/null +++ b/includes/migration/wc/class-fs-wc-migration.php @@ -0,0 +1,1118 @@ +init( WP_FS__NAMESPACE_WC ); + + $this->load_edd_entities( $license_id ); + } + + /** + * Pre-load all required EDD entities for complete migration process. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param int $license_id + */ + private function load_edd_entities( $license_id ) { + $download_id = get_post_meta( $license_id, '_edd_sl_download_id', true ); + $initial_payment_id = get_post_meta( $license_id, '_edd_sl_payment_id', true ); + $customer_id = edd_get_payment_customer_id( $initial_payment_id ); + + $this->_edd_license = get_post( $license_id ); + $this->_edd_download = new EDD_Download( $download_id ); + $this->_edd_customer = new EDD_Customer( $customer_id ); + $this->_edd_payment = new EDD_Payment( $initial_payment_id ); + + $this->_edd_subscription = $this->get_edd_subscription( + $download_id, + $initial_payment_id + ); + + if ( is_object( $this->_edd_subscription ) ) { + /** + * Load renewals data. + * + * @var WP_Post[] $edd_renewal_posts + */ + $edd_renewal_posts = $this->_edd_subscription->get_child_payments(); + + if ( is_array( $edd_renewal_posts ) && 0 < count( $edd_renewal_posts ) ) { + foreach ( $edd_renewal_posts as $edd_renewal_post ) { + $this->_edd_renewals[] = new EDD_Payment( $edd_renewal_post->ID ); + } + } + } + } + + #endregion + + #-------------------------------------------------------------------------------- + #region Helper Methods + #-------------------------------------------------------------------------------- + + /** + * Get EDD subscription entity when license associated with a subscription. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param int $download_id + * @param int $parent_payment_id + * + * @return \EDD_Subscription|false + */ + private function get_edd_subscription( $download_id, $parent_payment_id ) { + if ( ! class_exists( 'EDD_Recurring' ) ) { + // EDD recurring payments add-on isn't installed. + return false; + } + + /** + * We need to make sure the singleton is initiated, otherwise, + * EDD_Subscriptions_DB will not be found because the inclusion + * of the relevant file is executed in the instance init. + */ + EDD_Recurring::instance(); + + $subscriptions_db = new EDD_Subscriptions_DB(); + + $edd_subscriptions = $subscriptions_db->get_subscriptions( array( + 'product_id' => $download_id, + 'parent_payment_id' => $parent_payment_id + ) ); + + return ( is_array( $edd_subscriptions ) && 0 < count( $edd_subscriptions ) ) ? + $edd_subscriptions[0] : + false; + } + + #endregion + + /** + * Init install migration data before + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param array $local_install + */ + protected function set_local_install( $local_install ) { + $this->_edd_install_data = $local_install; + } + + #-------------------------------------------------------------------------------- + #region Local Data Getters + #-------------------------------------------------------------------------------- + + /** + * Local module ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_module_id() { + return $this->_edd_download->ID; + } + + /** + * Local license ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_license_id() { + return $this->_edd_license->ID; + } + + /** + * Local install ID. + * + * There's no module install concept nor entity in EDD, therefore, + * generate a unique ID based on the download ID and site canonized URL. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_install_id() { + /** + * Limit the ID to 32 chars since the entity mapping + * local_id column is limited to 32 chars. + */ + return substr( + $this->_edd_download->ID . '_' . + md5( $this->get_edd_canonized_site_home_url() ), + 0, + 32 + ); + } + + /** + * Local customer ID (not the WP user ID). + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_customer_id() { + return $this->_edd_customer->id; + } + + /** + * Local customer's email. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_customer_email() { + return $this->_edd_customer->email; + } + + /** + * Local billing details ID. + * + * EDD doesn't have a billing entity so associate it with the customer ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_billing_id() { + return $this->_edd_customer->id; + } + + /** + * Local pricing ID. + * + * EDD doesn't have a unique pricing ID. + * 1. When EDD SL is installed and the license is associated + * with a variable price, use the download ID with the variable + * price ID ("{download->id}:{price->id}"). + * 2. When EDD SL is NOT installed, use "{download->id}:0" as the pricing ID. + * 3. When EDD SL is installed but it's a legacy license that is NOT associated + * with variable price, find the price ID based on the license activations limit, + * and use "{download->id}:{price->id}". + * If the license activation quota is different from all the quotas in the prices + * then use the first price ID in the variable prices. The quota will be explicitly + * set using the `license_quota` API param. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_pricing_id() { + $price_id = 0; + + if ( edd_has_variable_prices( $this->_edd_download->ID ) ) { + + $price_id = (int) self::$_edd_sl->get_price_id( $this->_edd_license->ID ); + + if ( 0 === $price_id ) { + /** + * Couldn't find matching price ID which means it's a legacy license. + * + * Fetch the price ID that has the same license activations quota. + */ + $edd_prices = $this->_edd_download->get_prices(); + + $edd_license_activations_limit = self::$_edd_sl->get_license_limit( $this->_edd_download->ID, + $this->_edd_license->ID ); + + $price_found = false; + foreach ( $edd_prices as $id => $edd_price ) { + if ( $edd_license_activations_limit == $edd_price['license_limit'] ) { + $price_id = $id; + $price_found = true; + break; + } + } + + if ( ! $price_found ) { + /** + * If license limit isn't matching any of the prices, use the first + * price ID. + */ + reset( $edd_prices ); + $price_id = key( $edd_prices ); + } + } + } + + return $this->_edd_download->ID . ':' . $price_id; + } + + /** + * Local plan ID. + * + * Since EDD doesn't have a concept of plans and since we locally + * link all local pricing to the remote paid plan, use the local pricing ID + * as the local plan ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_paid_plan_id() { + return $this->get_local_pricing_id(); + } + + /** + * Local payment ID. When subscription return the initial payment ID. + * + * Since EDD's initial payment is associated to a cart and can contain + * multiple products, set the unique per download ID as the download ID + * with the payment ID combination. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_payment_id() { + // The initial payment can be associated to multiple downloads, + // therefore, we want to make it unique per module. + return $this->_edd_download->ID . ':' . $this->_edd_payment->ID; + } + + /** + * Check if the license is associated with a subscription. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return bool + */ + protected function local_is_subscription() { + return is_object( $this->_edd_subscription ); + } + + /** + * Local subscription ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + protected function get_local_subscription_id() { + return $this->_edd_subscription->id; + } + + /** + * Get renewals count. If license isn't associated with a subscription + * will simply return 0. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return int + */ + protected function get_local_renewals_count() { + return is_array( $this->_edd_renewals ) ? + count( $this->_edd_renewals ) : + 0; + } + + /** + * Local context specified renewal payment ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param int $index + * + * @return string + */ + protected function get_local_subscription_renewal_id( $index = 0 ) { + return $this->_edd_renewals[ $index ]->ID; + } + + #endregion + + #-------------------------------------------------------------------------------- + #region Data Mapping + #-------------------------------------------------------------------------------- + + #region Helper Methods + + /** + * Generate customer address for API. + * + * 1. First try to load address from payment details. + * 2. If empty, try to load address from customer details. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + private function get_customer_address_for_api() { + $user_info = $this->_edd_payment->user_info; + + $address = array( + 'line1' => '', + 'line2' => '', + 'city' => '', + 'state' => '', + 'country' => '', + 'zip' => '' + ); + + if ( ! empty( $user_info['address'] ) ) { + $address = wp_parse_args( $user_info['address'], $address ); + } else if ( ! empty( $this->_edd_customer->user_id ) ) { + // Enrich data with customer's address. + $customer_address = get_user_meta( $this->_edd_customer->user_id, '_edd_user_address', true ); + + $address = wp_parse_args( $customer_address, $address ); + } + + $api_address = array(); + if ( ! empty( $address['line1'] ) ) { + $api_address['address_street'] = $address['line1']; + } + if ( ! empty( $address['line2'] ) ) { + $api_address['address_apt'] = $address['line2']; + } + if ( ! empty( $address['city'] ) ) { + $api_address['address_city'] = $address['city']; + } + if ( ! empty( $address['state'] ) ) { + $api_address['address_state'] = $address['state']; + } + if ( ! empty( $address['country'] ) ) { + $api_address['address_country_code'] = strtolower( $address['country'] ); + } + if ( ! empty( $address['zip'] ) ) { + $api_address['address_zip'] = $address['zip']; + } + + return $api_address; + } + + /** + * Generate payment gross and tax for API based on given EDD payment. + * + * When initial payment associated with a cart that have multiple products, + * find the gross and tax for the product that is associated with the context + * license. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param EDD_Payment $edd_payment + * + * @return array + */ + private function get_payment_gross_and_tax_for_api( EDD_Payment $edd_payment ) { + $gross_and_vat = array(); + + if ( isset( $edd_payment->cart_details ) && + is_array( $edd_payment->cart_details ) && + 1 < count( $edd_payment->cart_details ) + ) { + /** + * Purchased multiple products in the same cart, find the gross & tax paid for the + * product associated with the license. + */ + $cart = $edd_payment->cart_details; + $context_edd_download_name = $this->_edd_download->get_name(); + foreach ( $cart as $edd_download ) { + if ( $context_edd_download_name === $edd_download['name'] ) { + $gross_and_vat['gross'] = $edd_download['price']; + + if ( is_numeric( $edd_download['tax'] ) && $edd_download['tax'] > 0 ) { + $gross_and_vat['vat'] = $edd_download['tax']; + } + + break; + } + } + } else { + /** + * Purchased only one product, get the gross & tax directly from the total + * payment. + */ + $gross_and_vat['gross'] = $edd_payment->total; + + if ( is_numeric( $edd_payment->tax ) && $edd_payment->tax > 0 ) { + $gross_and_vat['vat'] = $edd_payment->tax; + } + } + + return $gross_and_vat; + } + + /** + * Get license expiration in UTC datetime. + * If it's a lifetime license, return null. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string|null + */ + private function get_local_license_expiration() { + $license_expiration = self::$_edd_sl->get_license_expiration( $this->_edd_license->ID ); + + if ( 'lifetime' === $license_expiration ) { + return null; + } + + $timezone = date_default_timezone_get(); + + if ( 'UTC' !== $timezone ) { + // Temporary change time zone. + date_default_timezone_set( 'UTC' ); + } + + $formatted_license_expiration = date( WP_FSM__LOG_DATETIME_FORMAT, $license_expiration ); + + if ( 'UTC' !== $timezone ) { + // Revert timezone. + date_default_timezone_set( $timezone ); + } + + return $formatted_license_expiration; + } + + /** + * Try to get customer's IP address. + * + * - First try to get from the initial payment info. + * - Then, from the renewals. + * - Finally, check the EDD activations log and try to get it from there. + * + * If can't find it, return false. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string|false + */ + private function get_customer_ip() { + // Try to get IP from initial payment. + if ( ! empty( $this->_edd_payment->ip ) ) { + return $this->_edd_payment->ip; + } + + // Try to get IP from the subscription renewals. + if ( $this->local_is_subscription() && + is_array( $this->_edd_renewals ) + ) { + foreach ( $this->_edd_renewals as $edd_renewal ) { + if ( ! empty( $edd_renewal->ip ) ) { + return $edd_renewal->ip; + } + } + } + + // Try to fetch IP from license activation log. + $logs = edd_software_licensing()->get_license_logs( $this->_edd_license->ID ); + if ( is_array( $logs ) && 0 < count( $logs ) ) { + $activation_log_post_name_prefix = 'log-license-activated-'; + $activation_log_post_name_prefix_length = strlen( $activation_log_post_name_prefix ); + + foreach ( $logs as $log ) { + if ( ! has_term( 'renewal_notice', 'edd_log_type', $log->ID ) ) { + /** + * @var WP_Post $log + */ + if ( $activation_log_post_name_prefix === substr( $log->post_name, 0, + $activation_log_post_name_prefix_length ) + ) { + $activation_info = json_decode( get_post_field( 'post_content', $log->ID ) ); + if ( isset( $activation_info->REMOTE_ADDR ) && + ! empty( $activation_info->REMOTE_ADDR ) + ) { + return $activation_info->REMOTE_ADDR; + } + } + } + } + } + + return false; + } + + /** + * Check if sandbox purchase. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return bool + */ + private function local_is_sandbox_purchase() { + return ( 'live' !== $this->_edd_payment->mode ); + } + + /** + * Get purchase gateway. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + private function get_local_purchase_gateway() { + /** + * 1. Freemius doesn't have the concept of Test ("manual") gateway. + * 2. Freemius only have PayPal or CreditCard options at the moment. + */ + return ( 'paypal' === $this->_edd_payment->gateway ) ? + 'paypal' : + 'cc'; + } + + /** + * Get subscription gateway. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return string + */ + private function get_local_subscription_gateway() { + return $this->get_local_purchase_gateway(); + } + + /** + * Check if sandbox subscription. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return bool + */ + private function is_sandbox_subscription() { + return $this->local_is_sandbox_purchase(); + } + + /** + * Get billing cycle in months. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return int + */ + private function get_local_billing_cycle_in_months() { + switch ( $this->_edd_subscription->period ) { + case 'day': + case 'week': + // @todo The shortest supported billing period by Freemius is a Month. + case 'month': + return 1; + case 'quarter': + return 3; + case 'semi-year': + return 6; + case 'year': + default: + return 12; + } + } + + /** + * Get EDD payment's processing date. + * + * If payment was never completed, return the payment entity creation datetime. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param EDD_Payment $edd_payment + * + * @return string + */ + private function get_payment_process_date( EDD_Payment $edd_payment ) { + return ! empty( $edd_payment->completed_date ) ? + $edd_payment->completed_date : + $edd_payment->date; + } + + /** + * Get EDD payment's transaction ID. If empty, use "edd_payment_{payment_id}". + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param EDD_Payment $edd_payment + * + * @return string + */ + private function get_payment_transaction_id( EDD_Payment $edd_payment ) { + return ! empty( $edd_payment->transaction_id ) ? + $edd_payment->transaction_id : + 'edd_payment_' . $edd_payment->ID; + } + + /** + * Get payment data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param EDD_Payment $edd_payment + * + * @return array + */ + private function get_payment_by_edd_for_api( EDD_Payment $edd_payment ) { + $payment = array(); + $payment['processed_at'] = $this->get_payment_process_date( $edd_payment ); + $payment['payment_external_id'] = $this->get_payment_transaction_id( $edd_payment ); + + $payment = array_merge( $payment, $this->get_payment_gross_and_tax_for_api( $edd_payment ) ); + + return $payment; + } + + /** + * Generate site's URL based on how EDD stores the URLs in the + * license post meta ('_edd_sl_sites'). + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param string $url + * + * @return string + */ + private function get_edd_canonized_site_home_url( $url = '' ) { + if ( empty( $url ) ) { + $url = ! empty( $this->_edd_install_data['url'] ) ? + $this->_edd_install_data['url'] : + ''; + } + + if ( empty( $url ) ) { + + // Attempt to grab the URL from the user agent if no URL is specified + $domain = array_map( 'trim', explode( ';', $_SERVER['HTTP_USER_AGENT'] ) ); + $url = trim( $domain[1] ); + + } + + return trailingslashit( self::$_edd_sl->clean_site_url( $url ) ); + } + + /** + * Try to find installation date based on EDD license activation log. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return false|string + */ + private function get_local_install_datetime() { + $logs = edd_software_licensing()->get_license_logs( $this->_edd_license->ID ); + + if ( is_array( $logs ) && 0 < count( $logs ) ) { + $activation_log_post_name_prefix = 'log-license-activated-'; + $activation_log_post_name_prefix_length = strlen( $activation_log_post_name_prefix ); + $canonized_url = trim( $this->get_edd_canonized_site_home_url(), '/' ); + + foreach ( $logs as $log ) { + if ( ! has_term( 'renewal_notice', 'edd_log_type', $log->ID ) ) { + /** + * @var WP_Post $log + */ + if ( $activation_log_post_name_prefix === substr( $log->post_name, 0, + $activation_log_post_name_prefix_length ) + ) { + $activation_info = json_decode( get_post_field( 'post_content', $log->ID ) ); + if ( isset( $activation_info->HTTP_USER_AGENT ) ) { + if ( false !== strpos( $activation_info->HTTP_USER_AGENT, $canonized_url ) ) { + // Found matching URL activation. + return $log->post_date_gmt; + } + } + } + } + } + } + + return false; + } + + /** + * Get license quota. If unlimited license, return NULL. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return int|null + */ + private function get_license_quota() { + $quota = (int) self::$_edd_sl->get_license_limit( + $this->_edd_download->ID, + $this->_edd_license->ID + ); + + return ( $quota > 0 ) ? $quota : null; + } + + #endregion + + /** + * Generate customer details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_customer_for_api() { + $customer = array(); + $customer['email'] = $this->_edd_customer->email; + $customer['name'] = $this->_edd_customer->name; + $customer['is_verified'] = true; + $customer['send_verification_email'] = false; // Do NOT send verification emails. + $customer['password'] = wp_generate_password( 8 ); // Generate random 8 char pass for FS. + + $ip = $this->get_customer_ip(); + if ( ! empty( $ip ) ) { + $customer['ip'] = $ip; + } + + return $customer; + } + + /** + * Generate customer billing details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_customer_billing_for_api() { + $payment_meta = $this->_edd_payment->payment_meta; + $user_info = $this->_edd_payment->user_info; + + $billing = array(); + $billing['first'] = $user_info['first_name']; + $billing['last'] = $user_info['last_name']; + $billing['email'] = $payment_meta['email']; + + $billing = array_merge( $billing, $this->get_customer_address_for_api() ); + + return $billing; + } + + /** + * Generate install details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param number $license_id + * + * @return array + */ + protected function get_install_for_api( $license_id ) { + $install = array(); + $install['url'] = $this->_edd_install_data['site_url']; + $install['version'] = $this->_edd_install_data['plugin_version']; + $install['is_premium'] = $this->_edd_install_data['is_premium']; + $install['is_active'] = $this->_edd_install_data['is_active']; + $install['is_uninstalled'] = $this->_edd_install_data['is_uninstalled']; + $install['uid'] = $this->_edd_install_data['site_uid']; + $install['title'] = $this->_edd_install_data['site_name']; + $install['language'] = $this->_edd_install_data['language']; + $install['charset'] = $this->_edd_install_data['charset']; + $install['platform_version'] = $this->_edd_install_data['platform_version']; + $install['programming_language_version'] = $this->_edd_install_data['php_version']; + $install['license_id'] = $license_id; + + $install_at = $this->get_local_install_datetime(); + + if ( ! empty( $install_at ) ) { + $install['installed_at'] = $install_at; + } + + return $install; + } + + /** + * Generate purchase EU VAT details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_purchase_vat_for_api() { + $vat = array(); + + $address = $this->get_customer_address_for_api(); + + if ( ! empty( $address['address_country_code'] ) ) { + $vat['country_code'] = $address['address_country_code']; + } + + if ( class_exists( '\lyquidity\edd_vat\Actions' ) ) { + if ( ! empty( $this->_edd_customer->user_id ) ) { + $vat_id = \lyquidity\edd_vat\Actions::instance()->get_vat_number( '', + $this->_edd_customer->user_id ); + + if ( ! empty( $vat_id ) ) { + $vat['vat_id'] = $vat_id; + } + } + } + + return $vat; + } + + /** + * Generate purchase details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_purchase_for_api() { + $purchase = array(); + $purchase['billing_cycle'] = 0; + $purchase['payment_method'] = $this->get_local_purchase_gateway(); + $purchase['customer_external_id'] = 'edd_customer_' . $this->_edd_customer->id; + $purchase['license_key'] = self::$_edd_sl->get_license_key( $this->_edd_license->ID ); // Preserve the same keys. + $purchase['license_quota'] = $this->get_license_quota(); // Preserve license activations limit. + $purchase['processed_at'] = $this->get_payment_process_date( $this->_edd_payment ); + $purchase['payment_external_id'] = $this->get_payment_transaction_id( $this->_edd_payment ); + + // Set license expiration if not a lifetime license via a purchase. + $license_expiration = $this->get_local_license_expiration(); + if ( null !== $license_expiration ) { + $purchase['license_expires_at'] = $license_expiration; + } + + if ( $this->local_is_sandbox_purchase() ) { + $purchase['is_sandbox'] = true; + } + + $purchase = array_merge( $purchase, $this->get_payment_gross_and_tax_for_api( $this->_edd_payment ) ); + + $purchase = array_merge( $purchase, $this->get_purchase_vat_for_api() ); + + return $purchase; + } + + /** + * Generate onetime payment details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_onetime_payment_for_api() { + $payment = $this->get_payment_by_edd_for_api( $this->_edd_payment ); + + return $payment; + } + + /** + * Generate subscription details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_subscription_for_api() { + + $subscription = array(); + $subscription['payment_method'] = $this->get_local_subscription_gateway(); + $subscription['billing_cycle'] = $this->get_local_billing_cycle_in_months(); + $subscription['subscription_external_id'] = ! empty( $this->_edd_subscription->profile_id ) ? + $this->_edd_subscription->profile_id : + 'edd_subscription_' . $this->_edd_subscription->id; + $subscription['customer_external_id'] = 'edd_customer_' . $this->_edd_customer->id; + $subscription['next_payment'] = $this->_edd_subscription->get_expiration(); + $subscription['processed_at'] = $this->_edd_subscription->created; + $subscription['license_key'] = self::$_edd_sl->get_license_key( $this->_edd_license->ID ); // Preserve the same keys. + $subscription['license_quota'] = $this->get_license_quota(); // Preserve license activations limit. + + /** + * Set license expiration for cases when the subscription's next + * payment isn't matching the license expiration. + * + * Also allow migration of a lifetime license with a subscription. + */ + $subscription['license_expires_at'] = $this->get_local_license_expiration(); + + if ( $this->is_sandbox_subscription() ) { + $subscription['is_sandbox'] = true; + } + + // @todo Enrich API to accept is_cancelled as an optional argument during migration. + if ( 'cancelled' === $this->_edd_subscription->get_status() ) { + $subscription['is_cancelled'] = true; + } + + $subscription = array_merge( $subscription, $this->get_purchase_vat_for_api() ); + + return $subscription; + } + + /** + * Generate subscription initial payment details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @return array + */ + protected function get_initial_payment_for_api() { + $payment = $this->get_payment_by_edd_for_api( $this->_edd_payment ); + + $transaction_id = $this->_edd_payment->transaction_id; + + if ( empty( $transaction_id ) ) { + /** + * From some reason when the gateway is Stripe the initial payment + * transaction ID is stored as the transaction ID of the subscription. + */ + $transaction_id = $this->_edd_subscription->get_transaction_id(); + } + + if ( empty( $transaction_id ) ) { + // Fallback to EDD payment ID. + $transaction_id = 'edd_payment_' . $this->_edd_payment->ID; + } + + $payment['payment_external_id'] = $transaction_id; + $payment['is_extend_license'] = false; + + return $payment; + } + + /** + * Generate subscription renewal details from local data for API. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param int $index Renewal payment index. + * + * @return array + */ + protected function get_subscription_renewal_for_api( $index = 0 ) { + $renewal = $this->get_payment_by_edd_for_api( $this->_edd_renewals[ $index ] ); + + // Don't extend license on renewal. + $renewal['is_extend_license'] = false; + + return $renewal; + } + + #endregion + } From 62f38bdc7f83c97f9298ec9e40c1d99f4198051c Mon Sep 17 00:00:00 2001 From: Vova Feldman Date: Tue, 1 Nov 2016 11:01:22 -0400 Subject: [PATCH 02/22] Removed the minor EDD dependency left in the abstract migration endpoint. --- .../class-fs-migration-endpoint-abstract.php | 20 +++++++++++++++---- .../edd/class-fs-edd-migration-endpoint.php | 14 +++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/includes/migration/class-fs-migration-endpoint-abstract.php b/includes/migration/class-fs-migration-endpoint-abstract.php index 1f212e4..531a803 100644 --- a/includes/migration/class-fs-migration-endpoint-abstract.php +++ b/includes/migration/class-fs-migration-endpoint-abstract.php @@ -66,7 +66,7 @@ protected function init( $namespace ) { add_action( 'init', array( $this, 'maybe_process_api_request' ) ); } - // Reduce query load for EDD API calls + // Reduce query load for API calls. add_action( 'after_setup_theme', array( $this, 'reduce_query_load' ) ); add_action( 'admin_menu', array( &$this, '_add_submenu' ), 99999999 ); @@ -172,7 +172,7 @@ protected function link_entity( FS_Entity $entity, $local_entity_id ) { function get_remote_paid_plan_id( $local_module_id ) { return $this->get_remote_id( FS_Plan::get_type(), - $local_module_id . ':' . ( edd_has_variable_prices( $local_module_id ) ? '1' : '0' ) + $this->get_local_paid_plan_id( $local_module_id ) ); } @@ -357,7 +357,7 @@ function _save_settings() { } /** - * Get all EDD downloads mapped to FS objects for the settings page. + * Get all products mapped to FS objects for the settings page. * * @author Vova Feldman * @since 1.0.0 @@ -796,7 +796,7 @@ public function _sync_module_to_freemius_callback() { $local_module = $this->get_local_module_by_id( $local_module_id ); if ( false === $local_module ) { - // Post not exist or not an EDD download. + // Post not exist. $this->shoot_json_failure( "There's no local module with the specified ID ({$local_module_id})." ); } @@ -893,6 +893,18 @@ abstract protected function get_local_module_migration_manager( $local_module, F */ abstract protected function get_local_module_by_id( $local_module_id ); + /** + * Should return the local plan ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param string $local_module_id + * + * @return string + */ + abstract protected function get_local_paid_plan_id( $local_module_id ); + /** * Should return an array of all local modules. * diff --git a/includes/migration/edd/class-fs-edd-migration-endpoint.php b/includes/migration/edd/class-fs-edd-migration-endpoint.php index 6428660..2c7c49f 100644 --- a/includes/migration/edd/class-fs-edd-migration-endpoint.php +++ b/includes/migration/edd/class-fs-edd-migration-endpoint.php @@ -162,6 +162,20 @@ protected function get_local_module_by_id( $local_module_id ) { new EDD_Download( $local_module_id ); } + /** + * Return the EDD plan ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param string $local_module_id + * + * @return string + */ + protected function get_local_paid_plan_id( $local_module_id ) { + return $local_module_id . ':' . ( edd_has_variable_prices( $local_module_id ) ? '1' : '0' ); + } + /** * EDD Download slug (post_name). * From 13cc1991335033d53a6dcd88d84a173e4ae4e53f Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 8 Nov 2016 11:53:54 +0530 Subject: [PATCH 03/22] Removing EDD and adding WC in /includes/* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not including the `migrate` directory. Searched and replaced EDD naming with WC… --- includes/class-fs-entity-mapper.php | 2 +- includes/class-fs-logger.php | 2 +- includes/class-fs-option-manager.php | 2 +- includes/entities/class-fs-billing.php | 2 +- includes/entities/class-fs-developer.php | 2 +- includes/entities/class-fs-entity-map.php | 2 +- includes/entities/class-fs-entity.php | 2 +- includes/entities/class-fs-event.php | 2 +- includes/entities/class-fs-install.php | 2 +- includes/entities/class-fs-license.php | 2 +- includes/entities/class-fs-payment.php | 2 +- includes/entities/class-fs-plan.php | 2 +- includes/entities/class-fs-pricing.php | 2 +- includes/entities/class-fs-scope-entity.php | 2 +- includes/entities/class-fs-subscription.php | 2 +- includes/entities/class-fs-user.php | 2 +- .../edd/class-fs-edd-download-migration.php | 388 ------ .../edd/class-fs-edd-migration-endpoint.php | 357 ------ .../migration/edd/class-fs-edd-migration.php | 1118 ----------------- 19 files changed, 16 insertions(+), 1879 deletions(-) delete mode 100644 includes/migration/edd/class-fs-edd-download-migration.php delete mode 100644 includes/migration/edd/class-fs-edd-migration-endpoint.php delete mode 100644 includes/migration/edd/class-fs-edd-migration.php diff --git a/includes/class-fs-entity-mapper.php b/includes/class-fs-entity-mapper.php index 1532117..4af0221 100644 --- a/includes/class-fs-entity-mapper.php +++ b/includes/class-fs-entity-mapper.php @@ -28,7 +28,7 @@ class FS_Entity_Mapper { private $_table_name; /** - * @var string E.g. EDD or WOO + * @var string E.g. WC or WOO */ private $_namespace; diff --git a/includes/class-fs-logger.php b/includes/class-fs-logger.php index 7995278..3e1003e 100644 --- a/includes/class-fs-logger.php +++ b/includes/class-fs-logger.php @@ -1,6 +1,6 @@ > - */ - private $_edd_prices = array(); - - /** - * @var int - */ - private $_edd_free_price_id; - - /** - * @var bool - */ - private $_edd_has_paid_plan = false; - - /** - * @var array - */ - private $_edd_paid_plan_pricing = array(); - - #-------------------------------------------------------------------------------- - #region Init - #-------------------------------------------------------------------------------- - - function __construct( - FS_Developer $developer, - $module, - EDD_Download $download - ) { - $this->init( WP_FS__NAMESPACE_EDD, $developer, $module ); - - $this->_edd_download = $download; - - if ( $download->has_variable_prices() ) { - $this->_edd_prices = $download->get_prices(); - } else { - if ( class_exists( 'EDD_Recurring' ) ) { - $recurring = EDD_Recurring::is_recurring( $download->ID ); - $period = EDD_Recurring::get_period_single( $download->ID ); - } else { - $recurring = false; - $period = 'never'; - } - - $this->_edd_prices = array( - // Set the EDD price ID as ZERO when the download doesn't have variable prices. - 0 => array( - 'recurring' => $recurring ? 'yes' : 'no', - 'period' => $period, - 'license_limit' => 0, - 'amount' => $download->get_price(), - ) - ); - } - - $this->process_local_pricing(); - } - - #endregion - - #-------------------------------------------------------------------------------- - #region Helper Methods - #-------------------------------------------------------------------------------- - - /** - * Get billing cycle out of the variable price. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @param array $edd_price - * - * @return string - */ - private function get_billing_cycle( $edd_price ) { - $billing_cycle = 'lifetime'; - - if ( ! empty( $edd_price['recurring'] ) && - 'yes' === $edd_price['recurring'] && - ! empty( $edd_price['period'] ) && - 'never' !== $edd_price['period'] - ) { - switch ( $edd_price['period'] ) { - case 'year': - $billing_cycle = 'annual'; - break; - case 'month': - $billing_cycle = 'monthly'; - break; - default: - // @todo Throw an error when billing cycle is not supported. - $billing_cycle = 'monthly'; - break; - } - } - - return $billing_cycle; - } - - /** - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @param number $id - * - * @return string - */ - private function get_edd_unique_price_id( $id ) { - return $this->_edd_download->ID . ':' . $id; - } - - /** - * Aggregate EDD prices based on license limits. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - */ - private function process_local_pricing() { - $edd_paid_plan_pricing_by_licenses = array(); - - foreach ( $this->_edd_prices as $id => &$edd_price ) { - $licenses = (int) $edd_price['license_limit']; - - // Add price ID to data. - $edd_price['_id'] = $id; - - // Check if free plan. - $amount = floatval( $edd_price['amount'] ); - - if ( .0 >= $amount ) { - $this->_edd_free_price_id = $this->get_edd_unique_price_id( $id ); - continue; - } - - if ( ! isset( $edd_paid_plan_pricing_by_licenses[ $licenses ] ) ) { - $edd_paid_plan_pricing_by_licenses[ $licenses ] = array(); - } - - // Paid plan. - $edd_paid_plan_pricing_by_licenses[ $licenses ][] = $edd_price; - - $this->_edd_has_paid_plan = true; - } - - foreach ( $edd_paid_plan_pricing_by_licenses as $licenses => $edd_prices ) { - $pricing = array( - 'edd_prices_ids' => array() - ); - - $pricing['licenses'] = ( $licenses > 0 ) ? $licenses : null; - - foreach ( $edd_prices as $edd_price ) { - $amount = floatval( $edd_price['amount'] ); - - $billing_cycle = $this->get_billing_cycle( $edd_price ); - $pricing["{$billing_cycle}_price"] = $amount; - - // We need to store EDD price IDs list to link them with the pricing. - $pricing['edd_prices_ids'][] = $this->get_edd_unique_price_id( $edd_price['_id'] ); - } - - $this->_edd_paid_plan_pricing[] = $pricing; - } - } - - #endregion - - #-------------------------------------------------------------------------------- - #region EDD Required Data Getters - #-------------------------------------------------------------------------------- - - /** - * Get local module ID. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @return bool - */ - protected function get_local_module_id() { - return $this->_edd_download->ID; - } - - /** - * Check if download has a free plan. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @return bool - */ - protected function local_has_free_plan() { - return isset( $this->_edd_free_price_id ); - } - - /** - * Get free price ID as the plan ID since EDD doesn't have a concept of plans. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @return bool - */ - protected function get_local_free_plan_id() { - return $this->_edd_download->ID . ':' . $this->_edd_free_price_id . 'free'; - } - - /** - * Check download has any price that is not free. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @return bool - */ - protected function local_has_paid_plan() { - return $this->_edd_has_paid_plan; - } - - /** - * Return all the prices IDs that are paid and belong to the - * same plan (identical feature-set). - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @param int $index - * - * @return string[] - */ - protected function get_local_paid_plan_pricing_ids( $index ) { - return $this->_edd_paid_plan_pricing[ $index ]['edd_prices_ids']; - } - - /** - * Get paid plan associated pricing objects count. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @return bool - */ - protected function get_local_paid_plan_pricing_count() { - return count( $this->_edd_paid_plan_pricing ); - } - - /** - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @param int|null $licenses - * - * @return int|false - */ - protected function get_local_paid_plan_pricing_index_by_licenses( $licenses ) { - $index = 0; - - foreach ( $this->_edd_paid_plan_pricing as $edd_pricing ) { - if ( $licenses === $edd_pricing['licenses'] ) { - return $index; - } - - $index ++; - } - - return false; - } - - /** - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @param int $index - * - * @return int|null - */ - protected function get_local_paid_plan_pricing_licenses( $index ) { - return $this->_edd_paid_plan_pricing[ $index ]['licenses']; - } - - #endregion - - #-------------------------------------------------------------------------------- - #region Data Mapping for API - #-------------------------------------------------------------------------------- - - /** - * Generate module details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_module_for_api() { - $download_post = WP_Post::get_instance( $this->_edd_download->get_ID() ); - - return array( - 'slug' => $download_post->post_name, - 'title' => $this->_edd_download->get_name(), - 'type' => 'plugin', - 'business_model' => $this->get_local_business_model(), - 'created_at' => $download_post->post_date_gmt, - ); - } - - /** - * Generate free plan details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_free_plan_for_api() { - return array( - 'name' => 'free', - 'title' => 'Free', - ); - } - - /** - * Generate paid plan details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_paid_plan_for_api() { - return array( - 'name' => 'pro', - 'title' => 'Pro', - // By default create non-blocking plan. - 'is_block_features' => false, - 'is_https_support' => true, - // By default allow localhost activations. - 'is_free_localhost' => true, - // Set paid plan as featured. - 'is_featured' => true, - ); - } - - /** - * Generate paid plan pricing details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param int $index - * - * @return array - */ - protected function get_paid_plan_pricing_for_api( $index ) { - // Clone. - $pricing = $this->_edd_paid_plan_pricing[ $index ]; - - unset( $pricing['edd_prices_ids'] ); - - return $pricing; - } - - #endregion - } \ No newline at end of file diff --git a/includes/migration/edd/class-fs-edd-migration-endpoint.php b/includes/migration/edd/class-fs-edd-migration-endpoint.php deleted file mode 100644 index 2c7c49f..0000000 --- a/includes/migration/edd/class-fs-edd-migration-endpoint.php +++ /dev/null @@ -1,357 +0,0 @@ -init( WP_FS__NAMESPACE_EDD ); - - if ( false && ! defined( 'DOING_AJAX' ) ) { - $this->test_full_migration(); - } - } - - #-------------------------------------------------------------------------------- - #region Testing - #-------------------------------------------------------------------------------- - - /** - * Migrate license by ID. - * - * Note: This method is only for testing reasons. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @param int $license_id - * - * @throws Exception - */ - function migrate_license_by_id( $license_id ) { - require_once WP_FSM__DIR_MIGRATION . '/class-fs-migration-abstract.php'; - require_once WP_FSM__DIR_MIGRATION . '/edd/class-fs-edd-migration.php'; - - $migration = FS_EDD_Migration::instance( $license_id ); - $migration->set_api( $this->get_api() ); - $migration->do_migrate_license(); - } - - /** - * Test full install's license migration. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - */ - private function test_full_migration() { - $url = 'http://test9.freemius.com'; - $download_id = 25; - $license_key = '74062bc8b9cc256823f8f08d0f8feedf'; - - $params = array( - 'license_key' => $license_key, - 'module_id' => $download_id, - 'url' => $url, - 'site_url' => $url, - 'plugin_version' => '1.2.1', - 'site_uid' => $this->get_anonymous_id( $url ), - 'site_name' => 'Freemius Test', - 'platform_version' => get_bloginfo( 'version' ), - 'php_version' => phpversion(), - 'language' => get_bloginfo( 'language' ), - 'charset' => get_bloginfo( 'charset' ), - 'is_premium' => true, - 'is_active' => true, - 'is_uninstalled' => false, - ); - - $this->maybe_process_api_request( $params ); - } - - #endregion - - #-------------------------------------------------------------------------------- - #region Local Data Getters - #-------------------------------------------------------------------------------- - - /** - * Map EDD download into FS object. - * - * @author Vova Feldman (@svovaf) - * @since 1.0.0 - * - * @param EDD_Download $local_module - * - * @return FS_Plugin - */ - protected function local_to_remote_module( $local_module ) { - $module = new FS_Plugin(); - $module->id = $local_module->get_ID(); - $module->slug = $local_module->post_name; - $module->title = $local_module->get_name(); - - return $module; - } - - /** - * Get all EDD downloads. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_all_local_modules() { - /** - * @var WP_Post[] $downloads - */ - $downloads = get_posts( array( - 'post_type' => 'download', - 'posts_per_page' => - 1, - ) ); - - for ( $i = 0, $len = count( $downloads ); $i < $len; $i ++ ) { - $downloads[ $i ] = new EDD_Download( $downloads[ $i ]->ID ); - } - - return $downloads; - } - - /** - * Load EDD download by ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param string $local_module_id - * - * @return false|\EDD_Download - */ - protected function get_local_module_by_id( $local_module_id ) { - $download_post = WP_Post::get_instance( $local_module_id ); - - return ( empty( $download_post ) || 'download' !== $download_post->post_type ) ? - false : - new EDD_Download( $local_module_id ); - } - - /** - * Return the EDD plan ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param string $local_module_id - * - * @return string - */ - protected function get_local_paid_plan_id( $local_module_id ) { - return $local_module_id . ':' . ( edd_has_variable_prices( $local_module_id ) ? '1' : '0' ); - } - - /** - * EDD Download slug (post_name). - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param mixed $local_module - * - * @return string - */ - protected function get_local_module_slug( $local_module ) { - return $local_module->post_name; - } - - /** - * Return the instance of the EDD download migration manager. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param mixed $local_module - * @param FS_Plugin $module - * - * @return \FS_EDD_Download_Migration - */ - protected function get_local_module_migration_manager( $local_module, FS_Plugin $module = null ) { - require_once WP_FSM__DIR_MIGRATION . '/edd/class-fs-edd-download-migration.php'; - - if ( is_null( $module ) ) { - $module = $this->get_module_by_slug( $this->get_local_module_slug( $local_module ) ); - } - - return new FS_EDD_Download_Migration( - $this->get_developer(), - $module, - $local_module - ); - } - - #endregion - - /** - * Migrate install's license and return FS account's data (user + install). - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - * - * @throws FS_Endpoint_Exception - */ - protected function migrate_install_license() { - require_once WP_FSM__DIR_MIGRATION . '/class-fs-migration-abstract.php'; - require_once WP_FSM__DIR_MIGRATION . '/edd/class-fs-edd-migration.php'; - - $license_id = edd_software_licensing()->get_license_by_key( $this->get_param( 'license_key' ) ); - - $migration = FS_EDD_Migration::instance( $license_id ); - $migration->set_api( $this->get_api() ); - - // Migrate customer, purchase/subscription, billing and license. - $customer = $migration->do_migrate_license(); - - // Migrate plugin installation. - return $migration->do_migrate_install( $this->_request_data, $customer ); - } - - #-------------------------------------------------------------------------------- - #region API Request Params Validation - #-------------------------------------------------------------------------------- - - /** - * Validate EDD download license parameters. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @throws FS_Endpoint_Exception - */ - protected function validate_params() { - // Require download ID. - $this->require_unsigned_int( 'module_id' ); - - $download_id = $this->get_param( 'module_id' ); - $license_key = $this->get_param( 'license_key' ); - $url = $this->get_param( 'url' ); - - // Before checking license with EDD, make sure module is synced. - if (false === $this->get_remote_module_id( $download_id )){ - throw new FS_Endpoint_Exception( "Invalid download ID ({$download_id}).", 'invalid_download_id', 400 ); - } - - // Get EDD license state. - $edd_license_state = edd_software_licensing()->check_license( array( - 'item_id' => $download_id, - 'item_name' => '', - 'key' => $license_key, - 'url' => $url, - ) ); - - switch ( $edd_license_state ) { - case 'invalid': - // Invalid license key. - throw new FS_Endpoint_Exception( "Invalid license key ({$license_key}).", 'invalid_license_key', - 400 ); - case 'invalid_item_id': - // Invalid download ID. - throw new FS_Endpoint_Exception( "Invalid download ID ({$download_id}).", 'invalid_download_id', - 400 ); - /** - * Migrate expired license since all EDD licenses are not blocking. - */ - case 'expired': - /** - * License not yet activated. - * - * This use-case should not happen since if the client triggered a migration - * request with a valid license key, it means that the license was activated - * at least once. Hence, 'inactive' isn't possible. - */ - case 'inactive': - /** - * License was disabled, therefore, ... - * - * @todo what to do in that case? - */ - case 'disabled': - /** - * Migrate license & site. - * - * Based on the EDD SL logic this result is trigger when it's a production - * site (not localhost), and the site license wasn't activated. - * - * It can happen for example when the user trying to activate a license - * that is already fully utilized. - * - * @todo what to do in that case? - */ - case 'site_inactive': - /** - * Migrate license & site. - * - * License is valid and activated for the context site. - */ - case 'valid': - break; - case 'item_name_mismatch': - /** - * This use case should never happen since we check the license state - * based on the EDD download ID, not the name. - */ - break; - default: - // Unexpected license state. This case should never happen. - throw new FS_Endpoint_Exception( 'Unexpected EDD download license state.' ); - break; - } - } - - #endregion - } - - /** - * The main function responsible for returning the FS_EDD_Migration_Endpoint - * instance. - * - * Example: - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return FS_EDD_Migration_Endpoint The one true Easy_Digital_Downloads Instance - */ - function fs_edd_migration_manager() { - return FS_EDD_Migration_Endpoint::instance(); - } - - // Get Freemius EDD Migration running. - fs_edd_migration_manager(); \ No newline at end of file diff --git a/includes/migration/edd/class-fs-edd-migration.php b/includes/migration/edd/class-fs-edd-migration.php deleted file mode 100644 index 33713ca..0000000 --- a/includes/migration/edd/class-fs-edd-migration.php +++ /dev/null @@ -1,1118 +0,0 @@ -init( WP_FS__NAMESPACE_EDD ); - - $this->load_edd_entities( $license_id ); - } - - /** - * Pre-load all required EDD entities for complete migration process. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param int $license_id - */ - private function load_edd_entities( $license_id ) { - $download_id = get_post_meta( $license_id, '_edd_sl_download_id', true ); - $initial_payment_id = get_post_meta( $license_id, '_edd_sl_payment_id', true ); - $customer_id = edd_get_payment_customer_id( $initial_payment_id ); - - $this->_edd_license = get_post( $license_id ); - $this->_edd_download = new EDD_Download( $download_id ); - $this->_edd_customer = new EDD_Customer( $customer_id ); - $this->_edd_payment = new EDD_Payment( $initial_payment_id ); - - $this->_edd_subscription = $this->get_edd_subscription( - $download_id, - $initial_payment_id - ); - - if ( is_object( $this->_edd_subscription ) ) { - /** - * Load renewals data. - * - * @var WP_Post[] $edd_renewal_posts - */ - $edd_renewal_posts = $this->_edd_subscription->get_child_payments(); - - if ( is_array( $edd_renewal_posts ) && 0 < count( $edd_renewal_posts ) ) { - foreach ( $edd_renewal_posts as $edd_renewal_post ) { - $this->_edd_renewals[] = new EDD_Payment( $edd_renewal_post->ID ); - } - } - } - } - - #endregion - - #-------------------------------------------------------------------------------- - #region Helper Methods - #-------------------------------------------------------------------------------- - - /** - * Get EDD subscription entity when license associated with a subscription. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param int $download_id - * @param int $parent_payment_id - * - * @return \EDD_Subscription|false - */ - private function get_edd_subscription( $download_id, $parent_payment_id ) { - if ( ! class_exists( 'EDD_Recurring' ) ) { - // EDD recurring payments add-on isn't installed. - return false; - } - - /** - * We need to make sure the singleton is initiated, otherwise, - * EDD_Subscriptions_DB will not be found because the inclusion - * of the relevant file is executed in the instance init. - */ - EDD_Recurring::instance(); - - $subscriptions_db = new EDD_Subscriptions_DB(); - - $edd_subscriptions = $subscriptions_db->get_subscriptions( array( - 'product_id' => $download_id, - 'parent_payment_id' => $parent_payment_id - ) ); - - return ( is_array( $edd_subscriptions ) && 0 < count( $edd_subscriptions ) ) ? - $edd_subscriptions[0] : - false; - } - - #endregion - - /** - * Init install migration data before - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param array $local_install - */ - protected function set_local_install( $local_install ) { - $this->_edd_install_data = $local_install; - } - - #-------------------------------------------------------------------------------- - #region Local Data Getters - #-------------------------------------------------------------------------------- - - /** - * Local module ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_module_id() { - return $this->_edd_download->ID; - } - - /** - * Local license ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_license_id() { - return $this->_edd_license->ID; - } - - /** - * Local install ID. - * - * There's no module install concept nor entity in EDD, therefore, - * generate a unique ID based on the download ID and site canonized URL. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_install_id() { - /** - * Limit the ID to 32 chars since the entity mapping - * local_id column is limited to 32 chars. - */ - return substr( - $this->_edd_download->ID . '_' . - md5( $this->get_edd_canonized_site_home_url() ), - 0, - 32 - ); - } - - /** - * Local customer ID (not the WP user ID). - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_customer_id() { - return $this->_edd_customer->id; - } - - /** - * Local customer's email. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_customer_email() { - return $this->_edd_customer->email; - } - - /** - * Local billing details ID. - * - * EDD doesn't have a billing entity so associate it with the customer ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_billing_id() { - return $this->_edd_customer->id; - } - - /** - * Local pricing ID. - * - * EDD doesn't have a unique pricing ID. - * 1. When EDD SL is installed and the license is associated - * with a variable price, use the download ID with the variable - * price ID ("{download->id}:{price->id}"). - * 2. When EDD SL is NOT installed, use "{download->id}:0" as the pricing ID. - * 3. When EDD SL is installed but it's a legacy license that is NOT associated - * with variable price, find the price ID based on the license activations limit, - * and use "{download->id}:{price->id}". - * If the license activation quota is different from all the quotas in the prices - * then use the first price ID in the variable prices. The quota will be explicitly - * set using the `license_quota` API param. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_pricing_id() { - $price_id = 0; - - if ( edd_has_variable_prices( $this->_edd_download->ID ) ) { - - $price_id = (int) self::$_edd_sl->get_price_id( $this->_edd_license->ID ); - - if ( 0 === $price_id ) { - /** - * Couldn't find matching price ID which means it's a legacy license. - * - * Fetch the price ID that has the same license activations quota. - */ - $edd_prices = $this->_edd_download->get_prices(); - - $edd_license_activations_limit = self::$_edd_sl->get_license_limit( $this->_edd_download->ID, - $this->_edd_license->ID ); - - $price_found = false; - foreach ( $edd_prices as $id => $edd_price ) { - if ( $edd_license_activations_limit == $edd_price['license_limit'] ) { - $price_id = $id; - $price_found = true; - break; - } - } - - if ( ! $price_found ) { - /** - * If license limit isn't matching any of the prices, use the first - * price ID. - */ - reset( $edd_prices ); - $price_id = key( $edd_prices ); - } - } - } - - return $this->_edd_download->ID . ':' . $price_id; - } - - /** - * Local plan ID. - * - * Since EDD doesn't have a concept of plans and since we locally - * link all local pricing to the remote paid plan, use the local pricing ID - * as the local plan ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_paid_plan_id() { - return $this->get_local_pricing_id(); - } - - /** - * Local payment ID. When subscription return the initial payment ID. - * - * Since EDD's initial payment is associated to a cart and can contain - * multiple products, set the unique per download ID as the download ID - * with the payment ID combination. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_payment_id() { - // The initial payment can be associated to multiple downloads, - // therefore, we want to make it unique per module. - return $this->_edd_download->ID . ':' . $this->_edd_payment->ID; - } - - /** - * Check if the license is associated with a subscription. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return bool - */ - protected function local_is_subscription() { - return is_object( $this->_edd_subscription ); - } - - /** - * Local subscription ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - protected function get_local_subscription_id() { - return $this->_edd_subscription->id; - } - - /** - * Get renewals count. If license isn't associated with a subscription - * will simply return 0. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return int - */ - protected function get_local_renewals_count() { - return is_array( $this->_edd_renewals ) ? - count( $this->_edd_renewals ) : - 0; - } - - /** - * Local context specified renewal payment ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param int $index - * - * @return string - */ - protected function get_local_subscription_renewal_id( $index = 0 ) { - return $this->_edd_renewals[ $index ]->ID; - } - - #endregion - - #-------------------------------------------------------------------------------- - #region Data Mapping - #-------------------------------------------------------------------------------- - - #region Helper Methods - - /** - * Generate customer address for API. - * - * 1. First try to load address from payment details. - * 2. If empty, try to load address from customer details. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - private function get_customer_address_for_api() { - $user_info = $this->_edd_payment->user_info; - - $address = array( - 'line1' => '', - 'line2' => '', - 'city' => '', - 'state' => '', - 'country' => '', - 'zip' => '' - ); - - if ( ! empty( $user_info['address'] ) ) { - $address = wp_parse_args( $user_info['address'], $address ); - } else if ( ! empty( $this->_edd_customer->user_id ) ) { - // Enrich data with customer's address. - $customer_address = get_user_meta( $this->_edd_customer->user_id, '_edd_user_address', true ); - - $address = wp_parse_args( $customer_address, $address ); - } - - $api_address = array(); - if ( ! empty( $address['line1'] ) ) { - $api_address['address_street'] = $address['line1']; - } - if ( ! empty( $address['line2'] ) ) { - $api_address['address_apt'] = $address['line2']; - } - if ( ! empty( $address['city'] ) ) { - $api_address['address_city'] = $address['city']; - } - if ( ! empty( $address['state'] ) ) { - $api_address['address_state'] = $address['state']; - } - if ( ! empty( $address['country'] ) ) { - $api_address['address_country_code'] = strtolower( $address['country'] ); - } - if ( ! empty( $address['zip'] ) ) { - $api_address['address_zip'] = $address['zip']; - } - - return $api_address; - } - - /** - * Generate payment gross and tax for API based on given EDD payment. - * - * When initial payment associated with a cart that have multiple products, - * find the gross and tax for the product that is associated with the context - * license. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param EDD_Payment $edd_payment - * - * @return array - */ - private function get_payment_gross_and_tax_for_api( EDD_Payment $edd_payment ) { - $gross_and_vat = array(); - - if ( isset( $edd_payment->cart_details ) && - is_array( $edd_payment->cart_details ) && - 1 < count( $edd_payment->cart_details ) - ) { - /** - * Purchased multiple products in the same cart, find the gross & tax paid for the - * product associated with the license. - */ - $cart = $edd_payment->cart_details; - $context_edd_download_name = $this->_edd_download->get_name(); - foreach ( $cart as $edd_download ) { - if ( $context_edd_download_name === $edd_download['name'] ) { - $gross_and_vat['gross'] = $edd_download['price']; - - if ( is_numeric( $edd_download['tax'] ) && $edd_download['tax'] > 0 ) { - $gross_and_vat['vat'] = $edd_download['tax']; - } - - break; - } - } - } else { - /** - * Purchased only one product, get the gross & tax directly from the total - * payment. - */ - $gross_and_vat['gross'] = $edd_payment->total; - - if ( is_numeric( $edd_payment->tax ) && $edd_payment->tax > 0 ) { - $gross_and_vat['vat'] = $edd_payment->tax; - } - } - - return $gross_and_vat; - } - - /** - * Get license expiration in UTC datetime. - * If it's a lifetime license, return null. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string|null - */ - private function get_local_license_expiration() { - $license_expiration = self::$_edd_sl->get_license_expiration( $this->_edd_license->ID ); - - if ( 'lifetime' === $license_expiration ) { - return null; - } - - $timezone = date_default_timezone_get(); - - if ( 'UTC' !== $timezone ) { - // Temporary change time zone. - date_default_timezone_set( 'UTC' ); - } - - $formatted_license_expiration = date( WP_FSM__LOG_DATETIME_FORMAT, $license_expiration ); - - if ( 'UTC' !== $timezone ) { - // Revert timezone. - date_default_timezone_set( $timezone ); - } - - return $formatted_license_expiration; - } - - /** - * Try to get customer's IP address. - * - * - First try to get from the initial payment info. - * - Then, from the renewals. - * - Finally, check the EDD activations log and try to get it from there. - * - * If can't find it, return false. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string|false - */ - private function get_customer_ip() { - // Try to get IP from initial payment. - if ( ! empty( $this->_edd_payment->ip ) ) { - return $this->_edd_payment->ip; - } - - // Try to get IP from the subscription renewals. - if ( $this->local_is_subscription() && - is_array( $this->_edd_renewals ) - ) { - foreach ( $this->_edd_renewals as $edd_renewal ) { - if ( ! empty( $edd_renewal->ip ) ) { - return $edd_renewal->ip; - } - } - } - - // Try to fetch IP from license activation log. - $logs = edd_software_licensing()->get_license_logs( $this->_edd_license->ID ); - if ( is_array( $logs ) && 0 < count( $logs ) ) { - $activation_log_post_name_prefix = 'log-license-activated-'; - $activation_log_post_name_prefix_length = strlen( $activation_log_post_name_prefix ); - - foreach ( $logs as $log ) { - if ( ! has_term( 'renewal_notice', 'edd_log_type', $log->ID ) ) { - /** - * @var WP_Post $log - */ - if ( $activation_log_post_name_prefix === substr( $log->post_name, 0, - $activation_log_post_name_prefix_length ) - ) { - $activation_info = json_decode( get_post_field( 'post_content', $log->ID ) ); - if ( isset( $activation_info->REMOTE_ADDR ) && - ! empty( $activation_info->REMOTE_ADDR ) - ) { - return $activation_info->REMOTE_ADDR; - } - } - } - } - } - - return false; - } - - /** - * Check if sandbox purchase. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return bool - */ - private function local_is_sandbox_purchase() { - return ( 'live' !== $this->_edd_payment->mode ); - } - - /** - * Get purchase gateway. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - private function get_local_purchase_gateway() { - /** - * 1. Freemius doesn't have the concept of Test ("manual") gateway. - * 2. Freemius only have PayPal or CreditCard options at the moment. - */ - return ( 'paypal' === $this->_edd_payment->gateway ) ? - 'paypal' : - 'cc'; - } - - /** - * Get subscription gateway. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ - private function get_local_subscription_gateway() { - return $this->get_local_purchase_gateway(); - } - - /** - * Check if sandbox subscription. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return bool - */ - private function is_sandbox_subscription() { - return $this->local_is_sandbox_purchase(); - } - - /** - * Get billing cycle in months. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return int - */ - private function get_local_billing_cycle_in_months() { - switch ( $this->_edd_subscription->period ) { - case 'day': - case 'week': - // @todo The shortest supported billing period by Freemius is a Month. - case 'month': - return 1; - case 'quarter': - return 3; - case 'semi-year': - return 6; - case 'year': - default: - return 12; - } - } - - /** - * Get EDD payment's processing date. - * - * If payment was never completed, return the payment entity creation datetime. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param EDD_Payment $edd_payment - * - * @return string - */ - private function get_payment_process_date( EDD_Payment $edd_payment ) { - return ! empty( $edd_payment->completed_date ) ? - $edd_payment->completed_date : - $edd_payment->date; - } - - /** - * Get EDD payment's transaction ID. If empty, use "edd_payment_{payment_id}". - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param EDD_Payment $edd_payment - * - * @return string - */ - private function get_payment_transaction_id( EDD_Payment $edd_payment ) { - return ! empty( $edd_payment->transaction_id ) ? - $edd_payment->transaction_id : - 'edd_payment_' . $edd_payment->ID; - } - - /** - * Get payment data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param EDD_Payment $edd_payment - * - * @return array - */ - private function get_payment_by_edd_for_api( EDD_Payment $edd_payment ) { - $payment = array(); - $payment['processed_at'] = $this->get_payment_process_date( $edd_payment ); - $payment['payment_external_id'] = $this->get_payment_transaction_id( $edd_payment ); - - $payment = array_merge( $payment, $this->get_payment_gross_and_tax_for_api( $edd_payment ) ); - - return $payment; - } - - /** - * Generate site's URL based on how EDD stores the URLs in the - * license post meta ('_edd_sl_sites'). - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param string $url - * - * @return string - */ - private function get_edd_canonized_site_home_url( $url = '' ) { - if ( empty( $url ) ) { - $url = ! empty( $this->_edd_install_data['url'] ) ? - $this->_edd_install_data['url'] : - ''; - } - - if ( empty( $url ) ) { - - // Attempt to grab the URL from the user agent if no URL is specified - $domain = array_map( 'trim', explode( ';', $_SERVER['HTTP_USER_AGENT'] ) ); - $url = trim( $domain[1] ); - - } - - return trailingslashit( self::$_edd_sl->clean_site_url( $url ) ); - } - - /** - * Try to find installation date based on EDD license activation log. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return false|string - */ - private function get_local_install_datetime() { - $logs = edd_software_licensing()->get_license_logs( $this->_edd_license->ID ); - - if ( is_array( $logs ) && 0 < count( $logs ) ) { - $activation_log_post_name_prefix = 'log-license-activated-'; - $activation_log_post_name_prefix_length = strlen( $activation_log_post_name_prefix ); - $canonized_url = trim( $this->get_edd_canonized_site_home_url(), '/' ); - - foreach ( $logs as $log ) { - if ( ! has_term( 'renewal_notice', 'edd_log_type', $log->ID ) ) { - /** - * @var WP_Post $log - */ - if ( $activation_log_post_name_prefix === substr( $log->post_name, 0, - $activation_log_post_name_prefix_length ) - ) { - $activation_info = json_decode( get_post_field( 'post_content', $log->ID ) ); - if ( isset( $activation_info->HTTP_USER_AGENT ) ) { - if ( false !== strpos( $activation_info->HTTP_USER_AGENT, $canonized_url ) ) { - // Found matching URL activation. - return $log->post_date_gmt; - } - } - } - } - } - } - - return false; - } - - /** - * Get license quota. If unlimited license, return NULL. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return int|null - */ - private function get_license_quota() { - $quota = (int) self::$_edd_sl->get_license_limit( - $this->_edd_download->ID, - $this->_edd_license->ID - ); - - return ( $quota > 0 ) ? $quota : null; - } - - #endregion - - /** - * Generate customer details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_customer_for_api() { - $customer = array(); - $customer['email'] = $this->_edd_customer->email; - $customer['name'] = $this->_edd_customer->name; - $customer['is_verified'] = true; - $customer['send_verification_email'] = false; // Do NOT send verification emails. - $customer['password'] = wp_generate_password( 8 ); // Generate random 8 char pass for FS. - - $ip = $this->get_customer_ip(); - if ( ! empty( $ip ) ) { - $customer['ip'] = $ip; - } - - return $customer; - } - - /** - * Generate customer billing details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_customer_billing_for_api() { - $payment_meta = $this->_edd_payment->payment_meta; - $user_info = $this->_edd_payment->user_info; - - $billing = array(); - $billing['first'] = $user_info['first_name']; - $billing['last'] = $user_info['last_name']; - $billing['email'] = $payment_meta['email']; - - $billing = array_merge( $billing, $this->get_customer_address_for_api() ); - - return $billing; - } - - /** - * Generate install details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param number $license_id - * - * @return array - */ - protected function get_install_for_api( $license_id ) { - $install = array(); - $install['url'] = $this->_edd_install_data['site_url']; - $install['version'] = $this->_edd_install_data['plugin_version']; - $install['is_premium'] = $this->_edd_install_data['is_premium']; - $install['is_active'] = $this->_edd_install_data['is_active']; - $install['is_uninstalled'] = $this->_edd_install_data['is_uninstalled']; - $install['uid'] = $this->_edd_install_data['site_uid']; - $install['title'] = $this->_edd_install_data['site_name']; - $install['language'] = $this->_edd_install_data['language']; - $install['charset'] = $this->_edd_install_data['charset']; - $install['platform_version'] = $this->_edd_install_data['platform_version']; - $install['programming_language_version'] = $this->_edd_install_data['php_version']; - $install['license_id'] = $license_id; - - $install_at = $this->get_local_install_datetime(); - - if ( ! empty( $install_at ) ) { - $install['installed_at'] = $install_at; - } - - return $install; - } - - /** - * Generate purchase EU VAT details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_purchase_vat_for_api() { - $vat = array(); - - $address = $this->get_customer_address_for_api(); - - if ( ! empty( $address['address_country_code'] ) ) { - $vat['country_code'] = $address['address_country_code']; - } - - if ( class_exists( '\lyquidity\edd_vat\Actions' ) ) { - if ( ! empty( $this->_edd_customer->user_id ) ) { - $vat_id = \lyquidity\edd_vat\Actions::instance()->get_vat_number( '', - $this->_edd_customer->user_id ); - - if ( ! empty( $vat_id ) ) { - $vat['vat_id'] = $vat_id; - } - } - } - - return $vat; - } - - /** - * Generate purchase details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_purchase_for_api() { - $purchase = array(); - $purchase['billing_cycle'] = 0; - $purchase['payment_method'] = $this->get_local_purchase_gateway(); - $purchase['customer_external_id'] = 'edd_customer_' . $this->_edd_customer->id; - $purchase['license_key'] = self::$_edd_sl->get_license_key( $this->_edd_license->ID ); // Preserve the same keys. - $purchase['license_quota'] = $this->get_license_quota(); // Preserve license activations limit. - $purchase['processed_at'] = $this->get_payment_process_date( $this->_edd_payment ); - $purchase['payment_external_id'] = $this->get_payment_transaction_id( $this->_edd_payment ); - - // Set license expiration if not a lifetime license via a purchase. - $license_expiration = $this->get_local_license_expiration(); - if ( null !== $license_expiration ) { - $purchase['license_expires_at'] = $license_expiration; - } - - if ( $this->local_is_sandbox_purchase() ) { - $purchase['is_sandbox'] = true; - } - - $purchase = array_merge( $purchase, $this->get_payment_gross_and_tax_for_api( $this->_edd_payment ) ); - - $purchase = array_merge( $purchase, $this->get_purchase_vat_for_api() ); - - return $purchase; - } - - /** - * Generate onetime payment details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_onetime_payment_for_api() { - $payment = $this->get_payment_by_edd_for_api( $this->_edd_payment ); - - return $payment; - } - - /** - * Generate subscription details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_subscription_for_api() { - - $subscription = array(); - $subscription['payment_method'] = $this->get_local_subscription_gateway(); - $subscription['billing_cycle'] = $this->get_local_billing_cycle_in_months(); - $subscription['subscription_external_id'] = ! empty( $this->_edd_subscription->profile_id ) ? - $this->_edd_subscription->profile_id : - 'edd_subscription_' . $this->_edd_subscription->id; - $subscription['customer_external_id'] = 'edd_customer_' . $this->_edd_customer->id; - $subscription['next_payment'] = $this->_edd_subscription->get_expiration(); - $subscription['processed_at'] = $this->_edd_subscription->created; - $subscription['license_key'] = self::$_edd_sl->get_license_key( $this->_edd_license->ID ); // Preserve the same keys. - $subscription['license_quota'] = $this->get_license_quota(); // Preserve license activations limit. - - /** - * Set license expiration for cases when the subscription's next - * payment isn't matching the license expiration. - * - * Also allow migration of a lifetime license with a subscription. - */ - $subscription['license_expires_at'] = $this->get_local_license_expiration(); - - if ( $this->is_sandbox_subscription() ) { - $subscription['is_sandbox'] = true; - } - - // @todo Enrich API to accept is_cancelled as an optional argument during migration. - if ( 'cancelled' === $this->_edd_subscription->get_status() ) { - $subscription['is_cancelled'] = true; - } - - $subscription = array_merge( $subscription, $this->get_purchase_vat_for_api() ); - - return $subscription; - } - - /** - * Generate subscription initial payment details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return array - */ - protected function get_initial_payment_for_api() { - $payment = $this->get_payment_by_edd_for_api( $this->_edd_payment ); - - $transaction_id = $this->_edd_payment->transaction_id; - - if ( empty( $transaction_id ) ) { - /** - * From some reason when the gateway is Stripe the initial payment - * transaction ID is stored as the transaction ID of the subscription. - */ - $transaction_id = $this->_edd_subscription->get_transaction_id(); - } - - if ( empty( $transaction_id ) ) { - // Fallback to EDD payment ID. - $transaction_id = 'edd_payment_' . $this->_edd_payment->ID; - } - - $payment['payment_external_id'] = $transaction_id; - $payment['is_extend_license'] = false; - - return $payment; - } - - /** - * Generate subscription renewal details from local data for API. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param int $index Renewal payment index. - * - * @return array - */ - protected function get_subscription_renewal_for_api( $index = 0 ) { - $renewal = $this->get_payment_by_edd_for_api( $this->_edd_renewals[ $index ] ); - - // Don't extend license on renewal. - $renewal['is_extend_license'] = false; - - return $renewal; - } - - #endregion - } From b12adcf37ebc477099a41aa4bca06020528b98f1 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 8 Nov 2016 11:54:34 +0530 Subject: [PATCH 04/22] Admin settings and templates for WC --- .../migration/class-fs-migration-endpoint-abstract.php | 2 +- templates/admin-notice.php | 2 +- templates/settings.php | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/includes/migration/class-fs-migration-endpoint-abstract.php b/includes/migration/class-fs-migration-endpoint-abstract.php index 531a803..5f7e271 100644 --- a/includes/migration/class-fs-migration-endpoint-abstract.php +++ b/includes/migration/class-fs-migration-endpoint-abstract.php @@ -300,7 +300,7 @@ function _notices() { function _add_submenu() { // Add Freemius submenu item. $hook = add_submenu_page( - 'edit.php?post_type=download', + 'woocommerce', 'Freemius', 'Freemius', 'manage_options', diff --git a/templates/admin-notice.php b/templates/admin-notice.php index f7cc81e..64656dc 100644 --- a/templates/admin-notice.php +++ b/templates/admin-notice.php @@ -1,6 +1,6 @@
-

+

@@ -69,7 +69,7 @@ get_remote_module_id( $local_module->id ) ?> - + @@ -215,6 +215,7 @@ class=""> alert(''); } else { alert(''); + console.log( result ); } // Recover button's label. From 20ef81122fc31a7f32793fb053d584c477b8b029 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 8 Nov 2016 11:55:36 +0530 Subject: [PATCH 05/22] Sample file --- samples/module-migration.php | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/samples/module-migration.php b/samples/module-migration.php index ad49759..eee2ad8 100644 --- a/samples/module-migration.php +++ b/samples/module-migration.php @@ -1,11 +1,11 @@ 15, 'sslverify' => false, @@ -129,7 +129,7 @@ function do_my_edd2fs_license_migration( * @author Vova Feldman (@svovaf) * @since 1.0.0 * - * @param int $edd_download_id The context EDD download ID (from your store). + * @param int $edd_download_id The context WC download ID (from your store). * * @return bool Is successfully spawned the migration request. */ @@ -188,9 +188,9 @@ function spawn_my_edd2fs_license_migration( $edd_download_id ) { * @author Vova Feldman (@svovaf) * @since 1.0.0 * - * @param int $edd_download_id The context EDD download ID (from your store). - * @param string $edd_license_key The current site's EDD license key. - * @param string $edd_store_url Your EDD store URL. + * @param int $edd_download_id The context WC download ID (from your store). + * @param string $edd_license_key The current site's WC license key. + * @param string $edd_store_url Your WC store URL. * @param bool $is_blocking Special argument for testing. When false, will initiate the migration in the same * HTTP request. * @@ -262,7 +262,7 @@ function my_non_blocking_edd2fs_license_migration( } /** - * Try to activate EDD license. + * Try to activate WC license. * * @author Vova Feldman (@svovaf) * @since 1.0.0 @@ -274,14 +274,14 @@ function my_non_blocking_edd2fs_license_migration( function my_edd_activate_license( $license_key ) { // Call the custom API. $response = wp_remote_post( - MY__EDD_STORE_URL, + MY__WC_STORE_URL, array( 'timeout' => 15, 'sslverify' => false, 'body' => array( 'edd_action' => 'activate_license', 'license' => $license_key, - 'item_id' => MY__EDD_DOWNLOAD_ID, + 'item_id' => MY__WC_DOWNLOAD_ID, 'url' => home_url() ) ) @@ -296,7 +296,7 @@ function my_edd_activate_license( $license_key ) { $license_data = json_decode( wp_remote_retrieve_body( $response ) ); if ( 'valid' === $license_data->license ) { - // Store EDD license key. + // Store WC license key. update_option( 'edd_sample_license_key', $license_key ); } else { return false; @@ -307,7 +307,7 @@ function my_edd_activate_license( $license_key ) { /** * If installation failed due to license activation on Freemius try to - * activate the license on EDD first, and if successful, migrate the license + * activate the license on WC first, and if successful, migrate the license * with a blocking request. * * This method will only be triggered upon failed module installation. @@ -342,11 +342,11 @@ function my_try_migrate_on_activation( $response, $args ) { ( is_string( $response->error ) && false !== strpos( strtolower( $response->error ), 'license' ) ) ) { if ( my_edd_activate_license( $license_key ) ) { - // Successfully activated license on EDD, try to migrate to Freemius. + // Successfully activated license on WC, try to migrate to Freemius. if ( do_my_edd2fs_license_migration( - MY__EDD_DOWNLOAD_ID, + MY__WC_DOWNLOAD_ID, $license_key, - MY__EDD_STORE_URL, + MY__WC_STORE_URL, true ) ) { /** @@ -426,27 +426,27 @@ function fs_add_transient( $transient, $value, $expiration = 0 ) { #endregion if ( ! defined( 'DOING_CRON' ) ) { - // Pull EDD license key from storage. + // Pull WC license key from storage. $license_key = trim( get_option( 'edd_sample_license_key' ) ); if ( empty( $license_key ) ) { /** - * If no EDD license is set it might be one of the following: + * If no WC license is set it might be one of the following: * 1. User purchased module directly from Freemius. - * 2. User did purchase from EDD, but has never activated the license on this site. + * 2. User did purchase from WC, but has never activated the license on this site. * 3. User got access to the code without ever purchasing. * * In case it's reason #2, hook to Freemius `after_install_failure` event, and if * the installation failure resulted due to an issue with the license, try to - * activate the license on EDD first, and if works, migrate to Freemius right after. + * activate the license on WC first, and if works, migrate to Freemius right after. */ my_freemius()->add_filter( 'after_install_failure', 'my_try_migrate_on_activation', 10, 2 ); } else { if ( ! defined( 'DOING_AJAX' ) ) { my_non_blocking_edd2fs_license_migration( - MY__EDD_DOWNLOAD_ID, + MY__WC_DOWNLOAD_ID, $license_key, - MY__EDD_STORE_URL + MY__WC_STORE_URL ); } } From 0f915ac0f527ba0692c655c5673a7420f9209855 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 8 Nov 2016 11:55:55 +0530 Subject: [PATCH 06/22] Plugin root files naming --- config.php | 4 --- ...migration.php => freemius-wc-migration.php | 32 +++++++++---------- readme.txt | 4 +-- 3 files changed, 17 insertions(+), 23 deletions(-) rename freemius-edd-migration.php => freemius-wc-migration.php (57%) diff --git a/config.php b/config.php index b512bd7..3265db1 100644 --- a/config.php +++ b/config.php @@ -22,10 +22,6 @@ define( 'WP_FSM__MAIN_ENDPOINT', 'fs-api' ); } - if ( ! defined( 'WP_FS__NAMESPACE_EDD' ) ) { - define( 'WP_FS__NAMESPACE_EDD', 'EDD' ); - } - if ( ! defined( 'WP_FS__NAMESPACE_WC' ) ) { define( 'WP_FS__NAMESPACE_WC', 'WC' ); } diff --git a/freemius-edd-migration.php b/freemius-wc-migration.php similarity index 57% rename from freemius-edd-migration.php rename to freemius-wc-migration.php index ac9d004..2a1baa3 100644 --- a/freemius-edd-migration.php +++ b/freemius-wc-migration.php @@ -1,15 +1,15 @@ true ) ); @@ -36,19 +34,19 @@ function fs_edd_migration_init() { require_once __DIR__ . '/start.php'; // Load migration module. - require_once WP_FSM__DIR_MIGRATION . '/edd/class-fs-edd-migration-endpoint.php'; + require_once WP_FSM__DIR_MIGRATION . '/wc/class-fs-wc-migration-endpoint.php'; } - // Get Freemius EDD Migration running. - add_action( 'plugins_loaded', 'fs_edd_migration_init' ); + // Get Freemius WC Migration running. + add_action( 'plugins_loaded', 'fs_wc_migration_init' ); - function fs_edd_migration_auto_redirect() { + function fs_wc_migration_auto_redirect() { if ( ! function_exists( 'is_network_admin' ) || ! is_network_admin() ) { - if ( 'true' === get_option( 'fs_edd_migration_activated' ) ) { + if ( 'true' === get_option( 'fs_wc_migration_activated' ) ) { // Load config. require_once __DIR__ . '/start.php'; - update_option( 'fs_edd_migration_activated', null ); + update_option( 'fs_wc_migration_activated', null ); if ( fs_redirect( add_query_arg( array( 'post_type' => 'download', @@ -60,7 +58,7 @@ function fs_edd_migration_auto_redirect() { } } - add_action( 'admin_init', 'fs_edd_migration_auto_redirect' ); + add_action( 'admin_init', 'fs_wc_migration_auto_redirect' ); function fs_migration_plugin_activation() { require_once __DIR__ . '/includes/class-fs-entity-mapper.php'; @@ -69,7 +67,7 @@ function fs_migration_plugin_activation() { FS_Entity_Mapper::create_table(); // Hint the plugin that it was just activated. - update_option( "fs_edd_migration_activated", 'true' ); + update_option( "fs_wc_migration_activated", 'true' ); } register_activation_hook( __FILE__, 'fs_migration_plugin_activation' ); diff --git a/readme.txt b/readme.txt index db378ba..eeef931 100644 --- a/readme.txt +++ b/readme.txt @@ -1,4 +1,4 @@ -=== Freemius for Easy Digital Downloads Migration === +=== Freemius for WooCommerce API Manager Migration === Contributors: freemius Tags: freemius, fs, easy digital downloads, edd, bridge, adapter, add-on, plugins Requires at least: 3.9 @@ -7,7 +7,7 @@ Stable Tag: 1.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html -Lazy migration of EDD licenses to Freemius. +Lazy migration of WC licenses to Freemius. == Description == From 242ff6aecbf73ddf1c5c574924e863399cce9626 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 8 Nov 2016 12:00:20 +0530 Subject: [PATCH 07/22] Order array example --- tests/order.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/order.txt diff --git a/tests/order.txt b/tests/order.txt new file mode 100644 index 0000000..df416c2 --- /dev/null +++ b/tests/order.txt @@ -0,0 +1,23 @@ +# Contents of order array +array( + [user_id] => 2 + [order_id] => 21 + [order_key] => wc_order_abcdefgh12345 + [license_email] => theguy@example.com + [_api_software_title_parent] => Storefront Pro + [_api_software_title_var] => + [software_title] => Storefront Pro + [parent_product_id] => 10 + [variable_product_id] => + [current_version] => 3.5.0 + [_api_activations] => + [_api_activations_parent] => + [_api_update_permission] => yes + [is_variable_product] => no + [license_type] => + [expires] => + [_purchase_time] => 2016-11-03 05:40:08 + [_api_was_activated] => + [api_key] => wc_order_abcdefgh12345_am_rstuvwxy6789 + [product_id] => 10 +) \ No newline at end of file From e13b34c844c9af50e2e970c3c7fbf7e7b90974f7 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 8 Nov 2016 12:16:53 +0530 Subject: [PATCH 08/22] Plugin activation redirection and correcting namespace and correcting `WP_FS__NAMESPACE_WC` --- config.php | 2 +- freemius-wc-migration.php | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/config.php b/config.php index 3265db1..4cf886c 100644 --- a/config.php +++ b/config.php @@ -23,7 +23,7 @@ } if ( ! defined( 'WP_FS__NAMESPACE_WC' ) ) { - define( 'WP_FS__NAMESPACE_WC', 'WC' ); + define( 'WP_FS__NAMESPACE_WC', 'wc' ); } if ( ! defined( 'WP_FS__IS_PRODUCTION_MODE' ) ) { diff --git a/freemius-wc-migration.php b/freemius-wc-migration.php index 2a1baa3..efd57c8 100644 --- a/freemius-wc-migration.php +++ b/freemius-wc-migration.php @@ -48,10 +48,7 @@ function fs_wc_migration_auto_redirect() { update_option( 'fs_wc_migration_activated', null ); - if ( fs_redirect( add_query_arg( array( - 'post_type' => 'download', - 'page' => 'fs-migration', - ), admin_url( 'edit.php', 'admin' ) ) ) ) { + if ( fs_redirect( admin_url( 'admin.php?page=fs-migration' ) ) ) { exit; } } From cad67e1cc018d0af8435b30adc7ea259f1e0469e Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 8 Nov 2016 12:19:04 +0530 Subject: [PATCH 09/22] EDD > WC in file docs --- includes/migration/class-fs-module-migration-abstract.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/migration/class-fs-module-migration-abstract.php b/includes/migration/class-fs-module-migration-abstract.php index 19cb6be..a19a326 100644 --- a/includes/migration/class-fs-module-migration-abstract.php +++ b/includes/migration/class-fs-module-migration-abstract.php @@ -316,7 +316,7 @@ abstract protected function get_local_paid_plan_pricing_count(); * Get local paid plan pricing unique IDs. * * This case is relevant when there are different pricing objects for the - * same feature-set but different license activations limit like in EDD. + * same feature-set but different license activations limit like in WC. * * @author Vova Feldman (@svovaf) * @since 1.0.0 From c4089941611fc333bec49a113686fe6d7c6da870 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 8 Nov 2016 20:18:23 +0530 Subject: [PATCH 10/22] Updating FS_Entity_Map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New content provided by Vova over Slack… --- includes/entities/class-fs-entity-map.php | 72 ++++++++++++----------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/includes/entities/class-fs-entity-map.php b/includes/entities/class-fs-entity-map.php index d2d9d16..771af2c 100755 --- a/includes/entities/class-fs-entity-map.php +++ b/includes/entities/class-fs-entity-map.php @@ -1,42 +1,46 @@ Date: Tue, 8 Nov 2016 20:20:27 +0530 Subject: [PATCH 11/22] Subscriptions not implemented Exception... --- includes/migration/class-fs-migration-abstract.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/includes/migration/class-fs-migration-abstract.php b/includes/migration/class-fs-migration-abstract.php index 1f40e87..8a92920 100644 --- a/includes/migration/class-fs-migration-abstract.php +++ b/includes/migration/class-fs-migration-abstract.php @@ -1223,6 +1223,8 @@ protected function migrate_purchase( $customer_id ) { * @return \FS_Subscription */ protected function migrate_subscription( $customer_id ) { + throw new Exception( 'Subscriptions not implemented' ); + $result = $this->api_call( "/users/{$customer_id}/plans/{$this->get_plan_id()}/pricing/{$this->get_pricing_id()}.json", 'post', From 41c165ac3f148603e467cb5f73a4e06dbecdd87c Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Fri, 11 Nov 2016 12:03:21 +0530 Subject: [PATCH 12/22] WC Product to FS plugin and plans --- .../wc/class-fs-wc-download-migration.php | 150 ++++++------------ 1 file changed, 52 insertions(+), 98 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-download-migration.php b/includes/migration/wc/class-fs-wc-download-migration.php index bdede6c..de6f293 100644 --- a/includes/migration/wc/class-fs-wc-download-migration.php +++ b/includes/migration/wc/class-fs-wc-download-migration.php @@ -18,63 +18,41 @@ */ class FS_WC_Download_Migration extends FS_Module_Migration_Abstract { /** - * @var EDD_Download + * @var WC_Product_Variable|WC_Product */ - private $_edd_download; + private $_product; /** * @var array> */ - private $_edd_prices = array(); + private $_variations = array(); /** * @var int */ - private $_edd_free_price_id; + private $_free_price_id; /** * @var bool */ - private $_edd_has_paid_plan = false; + private $_has_paid_plan = false; /** * @var array */ - private $_edd_paid_plan_pricing = array(); + private $_plan_pricing = array(); #-------------------------------------------------------------------------------- #region Init #-------------------------------------------------------------------------------- - function __construct( - FS_Developer $developer, - $module, - EDD_Download $download - ) { - $this->init( WP_FS__NAMESPACE_WC, $developer, $module ); + function __construct( FS_Developer $developer, $module, WC_Product $product ) { + $this->_product = $product; - $this->_edd_download = $download; - - if ( $download->has_variable_prices() ) { - $this->_edd_prices = $download->get_prices(); - } else { - if ( class_exists( 'EDD_Recurring' ) ) { - $recurring = EDD_Recurring::is_recurring( $download->ID ); - $period = EDD_Recurring::get_period_single( $download->ID ); - } else { - $recurring = false; - $period = 'never'; - } + $this->init( WP_FS__NAMESPACE_WC, $developer, $module ); - $this->_edd_prices = array( - // Set the EDD price ID as ZERO when the download doesn't have variable prices. - 0 => array( - 'recurring' => $recurring ? 'yes' : 'no', - 'period' => $period, - 'license_limit' => 0, - 'amount' => $download->get_price(), - ) - ); + if ( $product->is_type('variable') ) { + $this->_variations = $this->_product->get_available_variations(); } $this->process_local_pricing(); @@ -92,33 +70,12 @@ function __construct( * @author Vova Feldman (@svovaf) * @since 1.0.0 * - * @param array $edd_price + * @todo Allow user to change this somehow * * @return string */ - private function get_billing_cycle( $edd_price ) { - $billing_cycle = 'lifetime'; - - if ( ! empty( $edd_price['recurring'] ) && - 'yes' === $edd_price['recurring'] && - ! empty( $edd_price['period'] ) && - 'never' !== $edd_price['period'] - ) { - switch ( $edd_price['period'] ) { - case 'year': - $billing_cycle = 'annual'; - break; - case 'month': - $billing_cycle = 'monthly'; - break; - default: - // @todo Throw an error when billing cycle is not supported. - $billing_cycle = 'monthly'; - break; - } - } - - return $billing_cycle; + private function get_billing_cycle( $id ) { + return 'annual'; } /** @@ -129,68 +86,67 @@ private function get_billing_cycle( $edd_price ) { * * @return string */ - private function get_edd_unique_price_id( $id ) { - return $this->_edd_download->ID . ':' . $id; + private function get_wc_unique_price_id( $id ) { + return $this->_product->id . ':' . $id; } /** - * Aggregate EDD prices based on license limits. + * Aggregate WC prices based on license limits. * * @author Vova Feldman (@svovaf) * @since 1.0.0 */ private function process_local_pricing() { - $edd_paid_plan_pricing_by_licenses = array(); + $plan_pricing = array(); - foreach ( $this->_edd_prices as $id => &$edd_price ) { - $licenses = (int) $edd_price['license_limit']; + foreach ( $this->_variations as &$variation ) { + $variation['_id'] = $id = $variation['variation_id']; - // Add price ID to data. - $edd_price['_id'] = $id; + $licenses = (int) get_post_meta( $id, '_api_activations', true ); - // Check if free plan. - $amount = floatval( $edd_price['amount'] ); + // Check if free plan. + $amount = floatval( $variation['display_regular_price'] ); if ( .0 >= $amount ) { - $this->_edd_free_price_id = $this->get_edd_unique_price_id( $id ); + $this->_free_price_id = $this->get_wc_unique_price_id( $id ); continue; } - if ( ! isset( $edd_paid_plan_pricing_by_licenses[ $licenses ] ) ) { - $edd_paid_plan_pricing_by_licenses[ $licenses ] = array(); + if ( ! isset( $plan_pricing[ $licenses ] ) ) { + $plan_pricing[ $licenses ] = array(); } // Paid plan. - $edd_paid_plan_pricing_by_licenses[ $licenses ][] = $edd_price; + $plan_pricing[ $licenses ][] = $variation; - $this->_edd_has_paid_plan = true; + $this->_has_paid_plan = true; } - foreach ( $edd_paid_plan_pricing_by_licenses as $licenses => $edd_prices ) { + foreach ( $plan_pricing as $licenses => $variations ) { $pricing = array( - 'edd_prices_ids' => array() + 'wc_prices_ids' => array() ); $pricing['licenses'] = ( $licenses > 0 ) ? $licenses : null; - foreach ( $edd_prices as $edd_price ) { - $amount = floatval( $edd_price['amount'] ); + foreach ( $variations as $variation ) { + $amount = floatval( $variation['display_regular_price'] ); - $billing_cycle = $this->get_billing_cycle( $edd_price ); + $billing_cycle = $this->get_billing_cycle( $variation['_id'] ); $pricing["{$billing_cycle}_price"] = $amount; - // We need to store EDD price IDs list to link them with the pricing. - $pricing['edd_prices_ids'][] = $this->get_edd_unique_price_id( $edd_price['_id'] ); + // We need to store WC price IDs list to link them with the pricing. + $pricing['wc_prices_ids'][] = $this->get_wc_unique_price_id( $variation['_id'] ); } - $this->_edd_paid_plan_pricing[] = $pricing; + $this->_plan_pricing[] = $pricing; } } #endregion #-------------------------------------------------------------------------------- - #region EDD Required Data Getters + #region WC Required Data Getters #-------------------------------------------------------------------------------- /** @@ -202,7 +158,7 @@ private function process_local_pricing() { * @return bool */ protected function get_local_module_id() { - return $this->_edd_download->ID; + return $this->_product->id; } /** @@ -214,11 +170,11 @@ protected function get_local_module_id() { * @return bool */ protected function local_has_free_plan() { - return isset( $this->_edd_free_price_id ); + return isset( $this->_free_price_id ); } /** - * Get free price ID as the plan ID since EDD doesn't have a concept of plans. + * Get free price ID as the plan ID since WC doesn't have a concept of plans. * * @author Vova Feldman (@svovaf) * @since 1.0.0 @@ -226,7 +182,7 @@ protected function local_has_free_plan() { * @return bool */ protected function get_local_free_plan_id() { - return $this->_edd_download->ID . ':' . $this->_edd_free_price_id . 'free'; + return $this->_product->id . ':' . $this->_free_price_id . 'free'; } /** @@ -238,7 +194,7 @@ protected function get_local_free_plan_id() { * @return bool */ protected function local_has_paid_plan() { - return $this->_edd_has_paid_plan; + return $this->_has_paid_plan; } /** @@ -253,7 +209,7 @@ protected function local_has_paid_plan() { * @return string[] */ protected function get_local_paid_plan_pricing_ids( $index ) { - return $this->_edd_paid_plan_pricing[ $index ]['edd_prices_ids']; + return $this->_plan_pricing[ $index ]['wc_prices_ids']; } /** @@ -265,7 +221,7 @@ protected function get_local_paid_plan_pricing_ids( $index ) { * @return bool */ protected function get_local_paid_plan_pricing_count() { - return count( $this->_edd_paid_plan_pricing ); + return count( $this->_plan_pricing ); } /** @@ -279,8 +235,8 @@ protected function get_local_paid_plan_pricing_count() { protected function get_local_paid_plan_pricing_index_by_licenses( $licenses ) { $index = 0; - foreach ( $this->_edd_paid_plan_pricing as $edd_pricing ) { - if ( $licenses === $edd_pricing['licenses'] ) { + foreach ( $this->_plan_pricing as $pricing ) { + if ( $licenses === $pricing['licenses'] ) { return $index; } @@ -299,7 +255,7 @@ protected function get_local_paid_plan_pricing_index_by_licenses( $licenses ) { * @return int|null */ protected function get_local_paid_plan_pricing_licenses( $index ) { - return $this->_edd_paid_plan_pricing[ $index ]['licenses']; + return $this->_plan_pricing[ $index ]['licenses']; } #endregion @@ -317,14 +273,12 @@ protected function get_local_paid_plan_pricing_licenses( $index ) { * @return array */ protected function get_module_for_api() { - $download_post = WP_Post::get_instance( $this->_edd_download->get_ID() ); - return array( - 'slug' => $download_post->post_name, - 'title' => $this->_edd_download->get_name(), + 'slug' => $this->_product->post->post_name, + 'title' => $this->_product->get_title(), 'type' => 'plugin', 'business_model' => $this->get_local_business_model(), - 'created_at' => $download_post->post_date_gmt, + 'created_at' => $this->_product->post->post_date_gmt, ); } @@ -377,9 +331,9 @@ protected function get_paid_plan_for_api() { */ protected function get_paid_plan_pricing_for_api( $index ) { // Clone. - $pricing = $this->_edd_paid_plan_pricing[ $index ]; + $pricing = $this->_plan_pricing[ $index ]; - unset( $pricing['edd_prices_ids'] ); + unset( $pricing['wc_prices_ids'] ); return $pricing; } From 867bbaf62ee559e418d7235e97db89c0503080cf Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Fri, 11 Nov 2016 12:05:31 +0530 Subject: [PATCH 13/22] Migration endpoint changes --- .../wc/class-fs-wc-migration-endpoint.php | 205 ++++++++---------- 1 file changed, 93 insertions(+), 112 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-migration-endpoint.php b/includes/migration/wc/class-fs-wc-migration-endpoint.php index 9c7a586..464fd32 100644 --- a/includes/migration/wc/class-fs-wc-migration-endpoint.php +++ b/includes/migration/wc/class-fs-wc-migration-endpoint.php @@ -14,6 +14,7 @@ /** * Class FS_WC_Migration_Endpoint + * @property stdClass $order Data from $_order using magic getter */ class FS_WC_Migration_Endpoint extends FS_Migration_Endpoint_Abstract { @@ -22,6 +23,27 @@ class FS_WC_Migration_Endpoint extends FS_Migration_Endpoint_Abstract { */ private static $_instance; + /** + * @var stdClass Order data Example in /tests/order.txt + */ + private $_order; + + /** + * @param string $name Property to return + * @return mixed|null Value if property exists else null + */ + public function __get( $name ) { + switch ( $name ) { + case 'order': + return $this->_order; + break; + default: + $this->get_param( $name ); + } + + return null; + } + public static function instance() { if ( ! isset( self::$_instance ) ) { self::$_instance = new FS_WC_Migration_Endpoint(); @@ -34,9 +56,7 @@ private function __construct() { $this->init( WP_FS__NAMESPACE_WC ); - if ( false && ! defined( 'DOING_AJAX' ) ) { - $this->test_full_migration(); - } + add_action( 'init', array( $this, 'maybe_test_full_migration' ) ); } #-------------------------------------------------------------------------------- @@ -72,26 +92,30 @@ function migrate_license_by_id( $license_id ) { * @author Vova Feldman (@svovaf) * @since 1.0.0 */ - private function test_full_migration() { - $url = 'http://test9.freemius.com'; - $download_id = 25; - $license_key = '74062bc8b9cc256823f8f08d0f8feedf'; + public function maybe_test_full_migration() { + if ( ! isset( $_GET['sfpfs-mig-test'] ) ) return; + + require_once WP_FSM__DIR_INCLUDES . '/class-fs-endpoint-exception.php'; + + $url = 'http://wp/sfpfs/usr'; + $email = 'theguy@example.com'; + $license_key = 'wc_order_58244d828095e_am_pLl1UmGbD0eb'; $params = array( - 'license_key' => $license_key, - 'module_id' => $download_id, - 'url' => $url, - 'site_url' => $url, - 'plugin_version' => '1.2.1', - 'site_uid' => $this->get_anonymous_id( $url ), - 'site_name' => 'Freemius Test', - 'platform_version' => get_bloginfo( 'version' ), - 'php_version' => phpversion(), - 'language' => get_bloginfo( 'language' ), - 'charset' => get_bloginfo( 'charset' ), - 'is_premium' => true, + "license_key" => $license_key, + "site_url" => $url, + "url" => $url, + "plugin_version" => '2.5.0', + "is_premium" => true, + "site_uid" => 'MoouenVlGG45', + "site_name" => 'GetFrappe', + "language" => 'en-US', + "charset" => 'UTF-8', + "platform_version" => '4.6.1', + "php_version" => '7.0.0', + "module_id" => 'Storefront Pro', + "email" => $email, 'is_active' => true, - 'is_uninstalled' => false, ); $this->maybe_process_api_request( $params ); @@ -103,21 +127,36 @@ private function test_full_migration() { #region Local Data Getters #-------------------------------------------------------------------------------- + /** + * Return the WC plan ID. + * + * @author Vova Feldman + * @since 1.0.0 + * + * @param string $product_id + * + * @return string + */ + protected function get_local_paid_plan_id( $product_id ) { + $product = wc_get_product( $product_id ); + return $product_id . ':' . ( $product->is_type( 'variable' ) ? '1' : '0' ); + } + /** * Map WC download into FS object. * * @author Vova Feldman (@svovaf) * @since 1.0.0 * - * @param EDD_Download $local_module + * @param WC_Product $local_module * * @return FS_Plugin */ protected function local_to_remote_module( $local_module ) { $module = new FS_Plugin(); - $module->id = $local_module->get_ID(); + $module->id = $local_module->id; $module->slug = $local_module->post_name; - $module->title = $local_module->get_name(); + $module->title = $local_module->get_title(); return $module; } @@ -135,12 +174,12 @@ protected function get_all_local_modules() { * @var WP_Post[] $downloads */ $downloads = get_posts( array( - 'post_type' => 'download', + 'post_type' => 'product', 'posts_per_page' => - 1, ) ); for ( $i = 0, $len = count( $downloads ); $i < $len; $i ++ ) { - $downloads[ $i ] = new EDD_Download( $downloads[ $i ]->ID ); + $downloads[ $i ] = wc_get_product( $downloads[ $i ]->ID ); } return $downloads; @@ -154,14 +193,11 @@ protected function get_all_local_modules() { * * @param string $local_module_id * - * @return false|\EDD_Download + * @return false|WC_Product */ protected function get_local_module_by_id( $local_module_id ) { - $download_post = WP_Post::get_instance( $local_module_id ); - - return ( empty( $download_post ) || 'download' !== $download_post->post_type ) ? - false : - new EDD_Download( $local_module_id ); + return ( 'product' !== get_post_type( $local_module_id ) ) ? false : + wc_get_product( $local_module_id ); } /** @@ -187,16 +223,16 @@ protected function get_local_module_slug( $local_module ) { * @param mixed $local_module * @param FS_Plugin $module * - * @return \FS_EDD_Download_Migration + * @return \FS_WC_Download_Migration */ protected function get_local_module_migration_manager( $local_module, FS_Plugin $module = null ) { - require_once WP_FSM__DIR_MIGRATION . '/edd/class-fs-edd-download-migration.php'; + require_once WP_FSM__DIR_MIGRATION . '/wc/class-fs-wc-download-migration.php'; if ( is_null( $module ) ) { $module = $this->get_module_by_slug( $this->get_local_module_slug( $local_module ) ); } - return new FS_EDD_Download_Migration( + return new FS_WC_Download_Migration( $this->get_developer(), $module, $local_module @@ -216,12 +252,12 @@ protected function get_local_module_migration_manager( $local_module, FS_Plugin * @throws FS_Endpoint_Exception */ protected function migrate_install_license() { + require_once WP_FSM__DIR_MIGRATION . '/class-fs-migration-abstract.php'; - require_once WP_FSM__DIR_MIGRATION . '/edd/class-fs-edd-migration.php'; + require_once WP_FSM__DIR_MIGRATION . '/wc/class-fs-wc-migration.php'; - $license_id = edd_software_licensing()->get_license_by_key( $this->get_param( 'license_key' ) ); + $migration = FS_WC_Migration::instance( $this ); - $migration = FS_WC_Migration::instance( $license_id ); $migration->set_api( $this->get_api() ); // Migrate customer, purchase/subscription, billing and license. @@ -236,7 +272,7 @@ protected function migrate_install_license() { #-------------------------------------------------------------------------------- /** - * Validate EDD download license parameters. + * Validate request parameters. * * @author Vova Feldman * @since 1.0.0 @@ -244,83 +280,28 @@ protected function migrate_install_license() { * @throws FS_Endpoint_Exception */ protected function validate_params() { - // Require download ID. - $this->require_unsigned_int( 'module_id' ); - - $download_id = $this->get_param( 'module_id' ); - $license_key = $this->get_param( 'license_key' ); - $url = $this->get_param( 'url' ); + // Software title ( which may not be same as product title ) is plugin identifier in WCAM + $plugin_title = $this->get_param( 'module_id' ); - // Before checking license with EDD, make sure module is synced. - if (false === $this->get_remote_module_id( $download_id )){ - throw new FS_Endpoint_Exception( "Invalid download ID ({$download_id}).", 'invalid_download_id', 400 ); - } + // Get order + $order_data = WCAM()->helpers->get_order_info_by_email_with_order_key( + $this->get_param( 'email' ), $this->get_param( 'license_key' ) + ); - // Get EDD license state. - $edd_license_state = edd_software_licensing()->check_license( array( - 'item_id' => $download_id, - 'item_name' => '', - 'key' => $license_key, - 'url' => $url, - ) ); + // + $order_data['product_id'] = WCAM()->helpers->get_product_id_by_software_title( + $plugin_title, $order_data['order_id'] + ); - switch ( $edd_license_state ) { - case 'invalid': - // Invalid license key. - throw new FS_Endpoint_Exception( "Invalid license key ({$license_key}).", 'invalid_license_key', - 400 ); - case 'invalid_item_id': - // Invalid download ID. - throw new FS_Endpoint_Exception( "Invalid download ID ({$download_id}).", 'invalid_download_id', - 400 ); - /** - * Migrate expired license since all EDD licenses are not blocking. - */ - case 'expired': - /** - * License not yet activated. - * - * This use-case should not happen since if the client triggered a migration - * request with a valid license key, it means that the license was activated - * at least once. Hence, 'inactive' isn't possible. - */ - case 'inactive': - /** - * License was disabled, therefore, ... - * - * @todo what to do in that case? - */ - case 'disabled': - /** - * Migrate license & site. - * - * Based on the EDD SL logic this result is trigger when it's a production - * site (not localhost), and the site license wasn't activated. - * - * It can happen for example when the user trying to activate a license - * that is already fully utilized. - * - * @todo what to do in that case? - */ - case 'site_inactive': - /** - * Migrate license & site. - * - * License is valid and activated for the context site. - */ - case 'valid': - break; - case 'item_name_mismatch': - /** - * This use case should never happen since we check the license state - * based on the EDD download ID, not the name. - */ - break; - default: - // Unexpected license state. This case should never happen. - throw new FS_Endpoint_Exception( 'Unexpected EDD download license state.' ); - break; + // Before checking license with WC, make sure module is synced. + if ( ! $order_data['product_id'] ){ + throw new FS_Endpoint_Exception( + "You don't have permission to access plugin with identifier {$plugin_title}.", 'invalid_plugin_identifier', + 400 + ); } + + $this->_order = (object) $order_data; } #endregion @@ -341,5 +322,5 @@ function fs_wc_migration_manager() { return FS_WC_Migration_Endpoint::instance(); } - // Get Freemius EDD Migration running. + // Get Freemius WC Migration running. fs_wc_migration_manager(); \ No newline at end of file From 9ab0cdb2dd99f81e0246c54eeda4b3a3ce008002 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Fri, 11 Nov 2016 12:05:51 +0530 Subject: [PATCH 14/22] FS_WC_Migration class --- .../migration/wc/class-fs-wc-migration.php | 498 +++++------------- 1 file changed, 142 insertions(+), 356 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-migration.php b/includes/migration/wc/class-fs-wc-migration.php index 090ab74..6e1d0ca 100644 --- a/includes/migration/wc/class-fs-wc-migration.php +++ b/includes/migration/wc/class-fs-wc-migration.php @@ -12,44 +12,28 @@ class FS_WC_Migration extends FS_Migration_Abstract { - /** - * @var EDD_Software_Licensing - */ - protected static $_edd_sl; - + /** @var FS_WC_Migration_Endpoint Instance */ + protected $ep; protected $_is_subscription = false; - #region EDD Entities + #region WC Entities - /** - * @var EDD_Download - */ - protected $_edd_download; + /** @var WC_Product Current product instance */ + protected $_product; - /** - * @var WP_Post - */ + /** @var WP_Post */ protected $_edd_license; - /** - * @var EDD_Customer - */ - protected $_edd_customer; - - /** - * @var EDD_Subscription - */ - protected $_edd_subscription; + /** @var null Not implemented */ + protected $_wc_subscription; - /** - * @var EDD_Payment - */ - protected $_edd_payment; + /** @var WC_Order Current order instance */ + protected $_wc_order; /** - * @var EDD_Payment[] + * @var WC_Payment[] */ protected $_edd_renewals = array(); @@ -64,26 +48,23 @@ class FS_WC_Migration extends FS_Migration_Abstract { #region Singleton #-------------------------------------------------------------------------------- - private static $_instances = array(); + /** @var FS_WC_Migration Instance */ + private static $_instance; /** * @author Vova Feldman * @since 1.0.0 * - * @param int $license_id + * @param FS_WC_Migration_Endpoint $endpoint * * @return FS_WC_Migration */ - public static function instance( $license_id ) { - if ( ! isset( self::$_edd_sl ) ) { - self::$_edd_sl = edd_software_licensing(); + public static function instance( $endpoint ) { + if ( ! isset( self::$_instance ) ) { + self::$_instance = new FS_WC_Migration( $endpoint ); } - if ( ! isset( self::$_instances[ $license_id ] ) ) { - self::$_instances[ $license_id ] = new FS_WC_Migration( $license_id ); - } - - return self::$_instances[ $license_id ]; + return self::$_instance; } #endregion @@ -92,59 +73,50 @@ public static function instance( $license_id ) { #region Init #-------------------------------------------------------------------------------- - private function __construct( $license_id ) { + private function __construct( $endpoint ) { $this->init( WP_FS__NAMESPACE_WC ); - $this->load_edd_entities( $license_id ); + $this->ep = $endpoint; + + $this->load_edd_entities(); } /** - * Pre-load all required EDD entities for complete migration process. + * Pre-load all required WC entities for complete migration process. * * @author Vova Feldman * @since 1.0.0 - * - * @param int $license_id */ - private function load_edd_entities( $license_id ) { - $download_id = get_post_meta( $license_id, '_edd_sl_download_id', true ); - $initial_payment_id = get_post_meta( $license_id, '_edd_sl_payment_id', true ); - $customer_id = edd_get_payment_customer_id( $initial_payment_id ); + private function load_edd_entities() { - $this->_edd_license = get_post( $license_id ); - $this->_edd_download = new EDD_Download( $download_id ); - $this->_edd_customer = new EDD_Customer( $customer_id ); - $this->_edd_payment = new EDD_Payment( $initial_payment_id ); + $this->_product = wc_get_product( $this->ep->order->product_id ); + $this->_wc_order = new WC_Order( $this->ep->order->order_id ); //@TODO Replace with WC equivalent for WC_Payment( $initial_payment_id ); - $this->_edd_subscription = $this->get_edd_subscription( + return; // Subscriptions not implemented + + $this->_wc_subscription = $this->get_edd_subscription( $download_id, $initial_payment_id ); - if ( is_object( $this->_edd_subscription ) ) { + if ( is_object( $this->_wc_subscription ) ) { /** * Load renewals data. * * @var WP_Post[] $edd_renewal_posts */ - $edd_renewal_posts = $this->_edd_subscription->get_child_payments(); + $edd_renewal_posts = $this->_wc_subscription->get_child_payments(); if ( is_array( $edd_renewal_posts ) && 0 < count( $edd_renewal_posts ) ) { foreach ( $edd_renewal_posts as $edd_renewal_post ) { - $this->_edd_renewals[] = new EDD_Payment( $edd_renewal_post->ID ); + $this->_edd_renewals[] = new WC_Payment( $edd_renewal_post->ID ); } } } } - #endregion - - #-------------------------------------------------------------------------------- - #region Helper Methods - #-------------------------------------------------------------------------------- - /** - * Get EDD subscription entity when license associated with a subscription. + * Get WC subscription entity when license associated with a subscription. * * @author Vova Feldman * @since 1.0.0 @@ -152,22 +124,24 @@ private function load_edd_entities( $license_id ) { * @param int $download_id * @param int $parent_payment_id * - * @return \EDD_Subscription|false + * @return \WC_Subscription|false */ private function get_edd_subscription( $download_id, $parent_payment_id ) { - if ( ! class_exists( 'EDD_Recurring' ) ) { - // EDD recurring payments add-on isn't installed. + throw new Exception( 'Not implemented' ); + + if ( ! class_exists( 'WC_Recurring' ) ) { + // WC recurring payments add-on isn't installed. return false; } /** * We need to make sure the singleton is initiated, otherwise, - * EDD_Subscriptions_DB will not be found because the inclusion + * WC_Subscriptions_DB will not be found because the inclusion * of the relevant file is executed in the instance init. */ - EDD_Recurring::instance(); + WC_Recurring::instance(); - $subscriptions_db = new EDD_Subscriptions_DB(); + $subscriptions_db = new WC_Subscriptions_DB(); $edd_subscriptions = $subscriptions_db->get_subscriptions( array( 'product_id' => $download_id, @@ -181,6 +155,11 @@ private function get_edd_subscription( $download_id, $parent_payment_id ) { #endregion + #-------------------------------------------------------------------------------- + #region Local Data Getters + #-------------------------------------------------------------------------------- + + /** * Init install migration data before * @@ -193,10 +172,6 @@ protected function set_local_install( $local_install ) { $this->_edd_install_data = $local_install; } - #-------------------------------------------------------------------------------- - #region Local Data Getters - #-------------------------------------------------------------------------------- - /** * Local module ID. * @@ -206,7 +181,7 @@ protected function set_local_install( $local_install ) { * @return string */ protected function get_local_module_id() { - return $this->_edd_download->ID; + return $this->ep->order->parent_product_id; } /** @@ -218,13 +193,13 @@ protected function get_local_module_id() { * @return string */ protected function get_local_license_id() { - return $this->_edd_license->ID; + return $this->ep->order->order_id; } /** * Local install ID. * - * There's no module install concept nor entity in EDD, therefore, + * There's no module install concept nor entity in WC, therefore, * generate a unique ID based on the download ID and site canonized URL. * * @author Vova Feldman @@ -237,12 +212,7 @@ protected function get_local_install_id() { * Limit the ID to 32 chars since the entity mapping * local_id column is limited to 32 chars. */ - return substr( - $this->_edd_download->ID . '_' . - md5( $this->get_edd_canonized_site_home_url() ), - 0, - 32 - ); + return $this->ep->site_uid; } /** @@ -254,7 +224,7 @@ protected function get_local_install_id() { * @return string */ protected function get_local_customer_id() { - return $this->_edd_customer->id; + return $this->ep->order->user_id; } /** @@ -266,13 +236,13 @@ protected function get_local_customer_id() { * @return string */ protected function get_local_customer_email() { - return $this->_edd_customer->email; + return $this->ep->order->license_email; } /** * Local billing details ID. * - * EDD doesn't have a billing entity so associate it with the customer ID. + * WC doesn't have a billing entity so associate it with the customer ID. * * @author Vova Feldman * @since 1.0.0 @@ -280,18 +250,18 @@ protected function get_local_customer_email() { * @return string */ protected function get_local_billing_id() { - return $this->_edd_customer->id; + return $this->ep->order->user_id; } /** * Local pricing ID. * - * EDD doesn't have a unique pricing ID. - * 1. When EDD SL is installed and the license is associated + * WC doesn't have a unique pricing ID. + * 1. When WC SL is installed and the license is associated * with a variable price, use the download ID with the variable * price ID ("{download->id}:{price->id}"). - * 2. When EDD SL is NOT installed, use "{download->id}:0" as the pricing ID. - * 3. When EDD SL is installed but it's a legacy license that is NOT associated + * 2. When WC SL is NOT installed, use "{download->id}:0" as the pricing ID. + * 3. When WC SL is installed but it's a legacy license that is NOT associated * with variable price, find the price ID based on the license activations limit, * and use "{download->id}:{price->id}". * If the license activation quota is different from all the quotas in the prices @@ -306,7 +276,7 @@ protected function get_local_billing_id() { protected function get_local_pricing_id() { $price_id = 0; - if ( edd_has_variable_prices( $this->_edd_download->ID ) ) { + if ( $this->_product->is_type( 'variable' ) ) { $price_id = (int) self::$_edd_sl->get_price_id( $this->_edd_license->ID ); @@ -316,9 +286,9 @@ protected function get_local_pricing_id() { * * Fetch the price ID that has the same license activations quota. */ - $edd_prices = $this->_edd_download->get_prices(); + $edd_prices = $this->_product->get_prices(); - $edd_license_activations_limit = self::$_edd_sl->get_license_limit( $this->_edd_download->ID, + $edd_license_activations_limit = self::$_edd_sl->get_license_limit( $this->_product->ID, $this->_edd_license->ID ); $price_found = false; @@ -341,21 +311,9 @@ protected function get_local_pricing_id() { } } - return $this->_edd_download->ID . ':' . $price_id; + return $this->ep->order->parent_product_id . ':' . $this->ep->order->product_id; } - /** - * Local plan ID. - * - * Since EDD doesn't have a concept of plans and since we locally - * link all local pricing to the remote paid plan, use the local pricing ID - * as the local plan ID. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string - */ protected function get_local_paid_plan_id() { return $this->get_local_pricing_id(); } @@ -363,7 +321,7 @@ protected function get_local_paid_plan_id() { /** * Local payment ID. When subscription return the initial payment ID. * - * Since EDD's initial payment is associated to a cart and can contain + * Since WC's initial payment is associated to a cart and can contain * multiple products, set the unique per download ID as the download ID * with the payment ID combination. * @@ -375,7 +333,7 @@ protected function get_local_paid_plan_id() { protected function get_local_payment_id() { // The initial payment can be associated to multiple downloads, // therefore, we want to make it unique per module. - return $this->_edd_download->ID . ':' . $this->_edd_payment->ID; + return 'wc_paid_' . $this->ep->order->product_id . '_' . $this->ep->order->order_id; } /** @@ -384,10 +342,10 @@ protected function get_local_payment_id() { * @author Vova Feldman * @since 1.0.0 * - * @return bool + * @return false */ protected function local_is_subscription() { - return is_object( $this->_edd_subscription ); + return false; } /** @@ -396,10 +354,10 @@ protected function local_is_subscription() { * @author Vova Feldman * @since 1.0.0 * - * @return string + * @return null No subscriptions */ protected function get_local_subscription_id() { - return $this->_edd_subscription->id; + return null; } /** @@ -412,9 +370,7 @@ protected function get_local_subscription_id() { * @return int */ protected function get_local_renewals_count() { - return is_array( $this->_edd_renewals ) ? - count( $this->_edd_renewals ) : - 0; + return 0; } /** @@ -428,7 +384,7 @@ protected function get_local_renewals_count() { * @return string */ protected function get_local_subscription_renewal_id( $index = 0 ) { - return $this->_edd_renewals[ $index ]->ID; + return null; } #endregion @@ -451,7 +407,7 @@ protected function get_local_subscription_renewal_id( $index = 0 ) { * @return array */ private function get_customer_address_for_api() { - $user_info = $this->_edd_payment->user_info; + $user_info = $this->_wc_order->get_address(); $address = array( 'line1' => '', @@ -464,9 +420,9 @@ private function get_customer_address_for_api() { if ( ! empty( $user_info['address'] ) ) { $address = wp_parse_args( $user_info['address'], $address ); - } else if ( ! empty( $this->_edd_customer->user_id ) ) { + } else if ( ! empty( $this->ep->order->user_id ) ) { // Enrich data with customer's address. - $customer_address = get_user_meta( $this->_edd_customer->user_id, '_edd_user_address', true ); + $customer_address = get_user_meta( $this->ep->order->user_id, '_edd_user_address', true ); $address = wp_parse_args( $customer_address, $address ); } @@ -495,7 +451,7 @@ private function get_customer_address_for_api() { } /** - * Generate payment gross and tax for API based on given EDD payment. + * Generate payment gross and tax for API based on given WC payment. * * When initial payment associated with a cart that have multiple products, * find the gross and tax for the product that is associated with the context @@ -504,43 +460,22 @@ private function get_customer_address_for_api() { * @author Vova Feldman * @since 1.0.0 * - * @param EDD_Payment $edd_payment + * @param WC_Order $order * * @return array */ - private function get_payment_gross_and_tax_for_api( EDD_Payment $edd_payment ) { - $gross_and_vat = array(); - - if ( isset( $edd_payment->cart_details ) && - is_array( $edd_payment->cart_details ) && - 1 < count( $edd_payment->cart_details ) - ) { - /** - * Purchased multiple products in the same cart, find the gross & tax paid for the - * product associated with the license. - */ - $cart = $edd_payment->cart_details; - $context_edd_download_name = $this->_edd_download->get_name(); - foreach ( $cart as $edd_download ) { - if ( $context_edd_download_name === $edd_download['name'] ) { - $gross_and_vat['gross'] = $edd_download['price']; - - if ( is_numeric( $edd_download['tax'] ) && $edd_download['tax'] > 0 ) { - $gross_and_vat['vat'] = $edd_download['tax']; - } + private function get_payment_gross_and_tax_for_api( WC_Order $order ) { + $order_item = $order->get_items(); - break; - } - } - } else { - /** - * Purchased only one product, get the gross & tax directly from the total - * payment. - */ - $gross_and_vat['gross'] = $edd_payment->total; + $gross_and_vat = array( + 'gross' => 0, + 'vat' => 0, + ); - if ( is_numeric( $edd_payment->tax ) && $edd_payment->tax > 0 ) { - $gross_and_vat['vat'] = $edd_payment->tax; + foreach( $order_item as $product ) { + if ( $product['product_id'] == $this->ep->order->product_id ) { + $gross_and_vat['gross'] = $product['line_total']; + $gross_and_vat['vat'] = $product['line_tax']; } } @@ -554,14 +489,14 @@ private function get_payment_gross_and_tax_for_api( EDD_Payment $edd_payment ) { * @author Vova Feldman * @since 1.0.0 * - * @return string|null + * @param WC_Order $order + * + * @return null|string */ - private function get_local_license_expiration() { - $license_expiration = self::$_edd_sl->get_license_expiration( $this->_edd_license->ID ); + private function get_local_license_expiration( $order = null ) { + if ( ! $order ) $order = $this->_wc_order; - if ( 'lifetime' === $license_expiration ) { - return null; - } + $time = strtotime( $order->order_date ); $timezone = date_default_timezone_get(); @@ -570,7 +505,8 @@ private function get_local_license_expiration() { date_default_timezone_set( 'UTC' ); } - $formatted_license_expiration = date( WP_FSM__LOG_DATETIME_FORMAT, $license_expiration ); + // Expire 1 year after purchase + $formatted_license_expiration = date( WP_FSM__LOG_DATETIME_FORMAT, $time + YEAR_IN_SECONDS ); if ( 'UTC' !== $timezone ) { // Revert timezone. @@ -580,77 +516,6 @@ private function get_local_license_expiration() { return $formatted_license_expiration; } - /** - * Try to get customer's IP address. - * - * - First try to get from the initial payment info. - * - Then, from the renewals. - * - Finally, check the EDD activations log and try to get it from there. - * - * If can't find it, return false. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return string|false - */ - private function get_customer_ip() { - // Try to get IP from initial payment. - if ( ! empty( $this->_edd_payment->ip ) ) { - return $this->_edd_payment->ip; - } - - // Try to get IP from the subscription renewals. - if ( $this->local_is_subscription() && - is_array( $this->_edd_renewals ) - ) { - foreach ( $this->_edd_renewals as $edd_renewal ) { - if ( ! empty( $edd_renewal->ip ) ) { - return $edd_renewal->ip; - } - } - } - - // Try to fetch IP from license activation log. - $logs = edd_software_licensing()->get_license_logs( $this->_edd_license->ID ); - if ( is_array( $logs ) && 0 < count( $logs ) ) { - $activation_log_post_name_prefix = 'log-license-activated-'; - $activation_log_post_name_prefix_length = strlen( $activation_log_post_name_prefix ); - - foreach ( $logs as $log ) { - if ( ! has_term( 'renewal_notice', 'edd_log_type', $log->ID ) ) { - /** - * @var WP_Post $log - */ - if ( $activation_log_post_name_prefix === substr( $log->post_name, 0, - $activation_log_post_name_prefix_length ) - ) { - $activation_info = json_decode( get_post_field( 'post_content', $log->ID ) ); - if ( isset( $activation_info->REMOTE_ADDR ) && - ! empty( $activation_info->REMOTE_ADDR ) - ) { - return $activation_info->REMOTE_ADDR; - } - } - } - } - } - - return false; - } - - /** - * Check if sandbox purchase. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return bool - */ - private function local_is_sandbox_purchase() { - return ( 'live' !== $this->_edd_payment->mode ); - } - /** * Get purchase gateway. * @@ -681,18 +546,6 @@ private function get_local_subscription_gateway() { return $this->get_local_purchase_gateway(); } - /** - * Check if sandbox subscription. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return bool - */ - private function is_sandbox_subscription() { - return $this->local_is_sandbox_purchase(); - } - /** * Get billing cycle in months. * @@ -702,7 +555,7 @@ private function is_sandbox_subscription() { * @return int */ private function get_local_billing_cycle_in_months() { - switch ( $this->_edd_subscription->period ) { + switch ( $this->_wc_subscription->period ) { case 'day': case 'week': // @todo The shortest supported billing period by Freemius is a Month. @@ -719,37 +572,20 @@ private function get_local_billing_cycle_in_months() { } /** - * Get EDD payment's processing date. - * - * If payment was never completed, return the payment entity creation datetime. + * Get WC payment's transaction ID. If empty, use "edd_payment_{payment_id}". * * @author Vova Feldman * @since 1.0.0 * - * @param EDD_Payment $edd_payment + * @param WC_Order $order * * @return string */ - private function get_payment_process_date( EDD_Payment $edd_payment ) { - return ! empty( $edd_payment->completed_date ) ? - $edd_payment->completed_date : - $edd_payment->date; - } - - /** - * Get EDD payment's transaction ID. If empty, use "edd_payment_{payment_id}". - * - * @author Vova Feldman - * @since 1.0.0 - * - * @param EDD_Payment $edd_payment - * - * @return string - */ - private function get_payment_transaction_id( EDD_Payment $edd_payment ) { - return ! empty( $edd_payment->transaction_id ) ? - $edd_payment->transaction_id : - 'edd_payment_' . $edd_payment->ID; + private function get_payment_transaction_id( WC_Order $order = null ) { + if ( ! $order ) { + $order = $this->_wc_order; + } + return "wc_payment_{$order->id}"; } /** @@ -758,22 +594,25 @@ private function get_payment_transaction_id( EDD_Payment $edd_payment ) { * @author Vova Feldman * @since 1.0.0 * - * @param EDD_Payment $edd_payment + * @param WC_Order $order * * @return array */ - private function get_payment_by_edd_for_api( EDD_Payment $edd_payment ) { + private function get_payment_by_edd_for_api( WC_Order $order = null ) { + if ( ! $order ) { + $order = $this->_wc_order; + } $payment = array(); - $payment['processed_at'] = $this->get_payment_process_date( $edd_payment ); - $payment['payment_external_id'] = $this->get_payment_transaction_id( $edd_payment ); + $payment['processed_at'] = $order->order_date; + $payment['payment_external_id'] = $this->get_payment_transaction_id( $order ); - $payment = array_merge( $payment, $this->get_payment_gross_and_tax_for_api( $edd_payment ) ); + $payment = array_merge( $payment, $this->get_payment_gross_and_tax_for_api( $order ) ); return $payment; } /** - * Generate site's URL based on how EDD stores the URLs in the + * Generate site's URL based on how WC stores the URLs in the * license post meta ('_edd_sl_sites'). * * @author Vova Feldman @@ -801,45 +640,6 @@ private function get_edd_canonized_site_home_url( $url = '' ) { return trailingslashit( self::$_edd_sl->clean_site_url( $url ) ); } - /** - * Try to find installation date based on EDD license activation log. - * - * @author Vova Feldman - * @since 1.0.0 - * - * @return false|string - */ - private function get_local_install_datetime() { - $logs = edd_software_licensing()->get_license_logs( $this->_edd_license->ID ); - - if ( is_array( $logs ) && 0 < count( $logs ) ) { - $activation_log_post_name_prefix = 'log-license-activated-'; - $activation_log_post_name_prefix_length = strlen( $activation_log_post_name_prefix ); - $canonized_url = trim( $this->get_edd_canonized_site_home_url(), '/' ); - - foreach ( $logs as $log ) { - if ( ! has_term( 'renewal_notice', 'edd_log_type', $log->ID ) ) { - /** - * @var WP_Post $log - */ - if ( $activation_log_post_name_prefix === substr( $log->post_name, 0, - $activation_log_post_name_prefix_length ) - ) { - $activation_info = json_decode( get_post_field( 'post_content', $log->ID ) ); - if ( isset( $activation_info->HTTP_USER_AGENT ) ) { - if ( false !== strpos( $activation_info->HTTP_USER_AGENT, $canonized_url ) ) { - // Found matching URL activation. - return $log->post_date_gmt; - } - } - } - } - } - } - - return false; - } - /** * Get license quota. If unlimited license, return NULL. * @@ -849,10 +649,7 @@ private function get_local_install_datetime() { * @return int|null */ private function get_license_quota() { - $quota = (int) self::$_edd_sl->get_license_limit( - $this->_edd_download->ID, - $this->_edd_license->ID - ); + $quota = (int) $this->ep->order->_api_activations_parent; return ( $quota > 0 ) ? $quota : null; } @@ -869,13 +666,13 @@ private function get_license_quota() { */ protected function get_customer_for_api() { $customer = array(); - $customer['email'] = $this->_edd_customer->email; - $customer['name'] = $this->_edd_customer->name; + $customer['email'] = $this->ep->order->license_email; + $customer['name'] = $this->_wc_order->billing_first_name . ' ' . $this->_wc_order->billing_last_name; $customer['is_verified'] = true; $customer['send_verification_email'] = false; // Do NOT send verification emails. $customer['password'] = wp_generate_password( 8 ); // Generate random 8 char pass for FS. - $ip = $this->get_customer_ip(); + $ip = $this->_wc_order->customer_ip_address; if ( ! empty( $ip ) ) { $customer['ip'] = $ip; } @@ -892,13 +689,11 @@ protected function get_customer_for_api() { * @return array */ protected function get_customer_billing_for_api() { - $payment_meta = $this->_edd_payment->payment_meta; - $user_info = $this->_edd_payment->user_info; $billing = array(); - $billing['first'] = $user_info['first_name']; - $billing['last'] = $user_info['last_name']; - $billing['email'] = $payment_meta['email']; + $billing['first'] = $this->_wc_order->billing_first_name; + $billing['last'] = $this->_wc_order->billing_last_name; + $billing['email'] = $this->_wc_order->billing_email; $billing = array_merge( $billing, $this->get_customer_address_for_api() ); @@ -930,7 +725,7 @@ protected function get_install_for_api( $license_id ) { $install['programming_language_version'] = $this->_edd_install_data['php_version']; $install['license_id'] = $license_id; - $install_at = $this->get_local_install_datetime(); + $install_at = $this->ep->order->_purchase_time; if ( ! empty( $install_at ) ) { $install['installed_at'] = $install_at; @@ -956,17 +751,6 @@ protected function get_purchase_vat_for_api() { $vat['country_code'] = $address['address_country_code']; } - if ( class_exists( '\lyquidity\edd_vat\Actions' ) ) { - if ( ! empty( $this->_edd_customer->user_id ) ) { - $vat_id = \lyquidity\edd_vat\Actions::instance()->get_vat_number( '', - $this->_edd_customer->user_id ); - - if ( ! empty( $vat_id ) ) { - $vat['vat_id'] = $vat_id; - } - } - } - return $vat; } @@ -981,12 +765,12 @@ protected function get_purchase_vat_for_api() { protected function get_purchase_for_api() { $purchase = array(); $purchase['billing_cycle'] = 0; - $purchase['payment_method'] = $this->get_local_purchase_gateway(); - $purchase['customer_external_id'] = 'edd_customer_' . $this->_edd_customer->id; - $purchase['license_key'] = self::$_edd_sl->get_license_key( $this->_edd_license->ID ); // Preserve the same keys. + $purchase['payment_method'] = $this->_wc_order->payment_method; + $purchase['customer_external_id'] = 'wc_customer_' . $this->ep->order->user_id; + $purchase['license_key'] = substr( $this->ep->order->api_key, 6 ); $purchase['license_quota'] = $this->get_license_quota(); // Preserve license activations limit. - $purchase['processed_at'] = $this->get_payment_process_date( $this->_edd_payment ); - $purchase['payment_external_id'] = $this->get_payment_transaction_id( $this->_edd_payment ); + $purchase['processed_at'] = $this->_wc_order->order_date; + $purchase['payment_external_id'] = $this->get_payment_transaction_id(); // Set license expiration if not a lifetime license via a purchase. $license_expiration = $this->get_local_license_expiration(); @@ -994,14 +778,16 @@ protected function get_purchase_for_api() { $purchase['license_expires_at'] = $license_expiration; } - if ( $this->local_is_sandbox_purchase() ) { - $purchase['is_sandbox'] = true; + if ( ! $purchase['payment_method'] ) { + $purchase['payment_method'] = 'cc'; } - $purchase = array_merge( $purchase, $this->get_payment_gross_and_tax_for_api( $this->_edd_payment ) ); + $purchase = array_merge( $purchase, $this->get_payment_gross_and_tax_for_api( $this->_wc_order ) ); $purchase = array_merge( $purchase, $this->get_purchase_vat_for_api() ); + print_awesome_r( $purchase ); + return $purchase; } @@ -1025,19 +811,21 @@ protected function get_onetime_payment_for_api() { * @author Vova Feldman * @since 1.0.0 * - * @return array - */ + * @return null + egion Helper Methods */ protected function get_subscription_for_api() { + throw new Exception( 'Not implemented' ); + $subscription = array(); $subscription['payment_method'] = $this->get_local_subscription_gateway(); $subscription['billing_cycle'] = $this->get_local_billing_cycle_in_months(); - $subscription['subscription_external_id'] = ! empty( $this->_edd_subscription->profile_id ) ? - $this->_edd_subscription->profile_id : - 'edd_subscription_' . $this->_edd_subscription->id; + $subscription['subscription_external_id'] = ! empty( $this->_wc_subscription->profile_id ) ? + $this->_wc_subscription->profile_id : + 'edd_subscription_' . $this->_wc_subscription->id; $subscription['customer_external_id'] = 'edd_customer_' . $this->_edd_customer->id; - $subscription['next_payment'] = $this->_edd_subscription->get_expiration(); - $subscription['processed_at'] = $this->_edd_subscription->created; + $subscription['next_payment'] = $this->_wc_subscription->get_expiration(); + $subscription['processed_at'] = $this->_wc_subscription->created; $subscription['license_key'] = self::$_edd_sl->get_license_key( $this->_edd_license->ID ); // Preserve the same keys. $subscription['license_quota'] = $this->get_license_quota(); // Preserve license activations limit. @@ -1049,12 +837,8 @@ protected function get_subscription_for_api() { */ $subscription['license_expires_at'] = $this->get_local_license_expiration(); - if ( $this->is_sandbox_subscription() ) { - $subscription['is_sandbox'] = true; - } - // @todo Enrich API to accept is_cancelled as an optional argument during migration. - if ( 'cancelled' === $this->_edd_subscription->get_status() ) { + if ( 'cancelled' === $this->_wc_subscription->get_status() ) { $subscription['is_cancelled'] = true; } @@ -1081,11 +865,11 @@ protected function get_initial_payment_for_api() { * From some reason when the gateway is Stripe the initial payment * transaction ID is stored as the transaction ID of the subscription. */ - $transaction_id = $this->_edd_subscription->get_transaction_id(); + $transaction_id = $this->_wc_subscription->get_transaction_id(); } if ( empty( $transaction_id ) ) { - // Fallback to EDD payment ID. + // Fallback to WC payment ID. $transaction_id = 'edd_payment_' . $this->_edd_payment->ID; } @@ -1106,6 +890,8 @@ protected function get_initial_payment_for_api() { * @return array */ protected function get_subscription_renewal_for_api( $index = 0 ) { + throw new Exception( 'Not implemented' ); + $renewal = $this->get_payment_by_edd_for_api( $this->_edd_renewals[ $index ] ); // Don't extend license on renewal. From 2aa8aaef01cad3801baaf28dfd0ba3ca173a9ed6 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Fri, 11 Nov 2016 15:18:04 +0530 Subject: [PATCH 15/22] Undefined function Call to undefined function print_awesome_r() --- includes/migration/wc/class-fs-wc-migration.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-migration.php b/includes/migration/wc/class-fs-wc-migration.php index 6e1d0ca..589600a 100644 --- a/includes/migration/wc/class-fs-wc-migration.php +++ b/includes/migration/wc/class-fs-wc-migration.php @@ -786,8 +786,6 @@ protected function get_purchase_for_api() { $purchase = array_merge( $purchase, $this->get_purchase_vat_for_api() ); - print_awesome_r( $purchase ); - return $purchase; } From cabff66e9b67b2460d88f9f8a7c4961fa73388af Mon Sep 17 00:00:00 2001 From: Vova Feldman Date: Fri, 11 Nov 2016 11:49:01 -0500 Subject: [PATCH 16/22] This is an abstract class, so it shouldn't be specifically associated with any platform. --- includes/migration/class-fs-module-migration-abstract.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/migration/class-fs-module-migration-abstract.php b/includes/migration/class-fs-module-migration-abstract.php index a19a326..52415d4 100644 --- a/includes/migration/class-fs-module-migration-abstract.php +++ b/includes/migration/class-fs-module-migration-abstract.php @@ -316,7 +316,7 @@ abstract protected function get_local_paid_plan_pricing_count(); * Get local paid plan pricing unique IDs. * * This case is relevant when there are different pricing objects for the - * same feature-set but different license activations limit like in WC. + * same feature-set but different license activations limit like in EDD or WC. * * @author Vova Feldman (@svovaf) * @since 1.0.0 @@ -1017,4 +1017,4 @@ private function delete_plan( $plan_id ) { } #endregion - } \ No newline at end of file + } From b46abb689527c5b45e397fac77d7b85fe9a4483d Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 15 Nov 2016 17:13:42 +0530 Subject: [PATCH 17/22] Resolving conflicts --- includes/entities/class-fs-entity-map.php | 19 ------------------- samples/module-migration.php | 16 ---------------- 2 files changed, 35 deletions(-) diff --git a/includes/entities/class-fs-entity-map.php b/includes/entities/class-fs-entity-map.php index f2580db..771af2c 100755 --- a/includes/entities/class-fs-entity-map.php +++ b/includes/entities/class-fs-entity-map.php @@ -14,7 +14,6 @@ class FS_Entity_Map extends FS_Entity { #region Properties -<<<<<<< HEAD /** * @var string */ @@ -31,24 +30,6 @@ class FS_Entity_Map extends FS_Entity { * @var number */ public $remote_id; -======= - /** - * @var string - */ - public $namespace; - /** - * @var string - */ - public $entity_type; - /** - * @var string - */ - public $local_id; - /** - * @var number - */ - public $remote_id; ->>>>>>> Freemius/feature/woocommerce #endregion Properties diff --git a/samples/module-migration.php b/samples/module-migration.php index eaeab07..ccfb762 100644 --- a/samples/module-migration.php +++ b/samples/module-migration.php @@ -445,21 +445,6 @@ function fs_add_transient( $transient, $value, $expiration = 0 ) { // Pull WC license key from storage. $license_key = trim( get_option( 'edd_sample_license_key' ) ); -<<<<<<< HEAD - if ( empty( $license_key ) ) { - /** - * If no WC license is set it might be one of the following: - * 1. User purchased module directly from Freemius. - * 2. User did purchase from WC, but has never activated the license on this site. - * 3. User got access to the code without ever purchasing. - * - * In case it's reason #2, hook to Freemius `after_install_failure` event, and if - * the installation failure resulted due to an issue with the license, try to - * activate the license on WC first, and if works, migrate to Freemius right after. - */ - my_freemius()->add_filter( 'after_install_failure', 'my_try_migrate_on_activation', 10, 2 ); - } else { -======= /** * If no EDD license is set it might be one of the following: * 1. User purchased module directly from Freemius. @@ -474,7 +459,6 @@ function fs_add_transient( $transient, $value, $expiration = 0 ) { my_freemius()->add_filter( 'after_install_failure', 'my_try_migrate_on_activation', 10, 2 ); if ( ! empty( $license_key ) ) { ->>>>>>> Freemius/feature/woocommerce if ( ! defined( 'DOING_AJAX' ) ) { my_non_blocking_edd2fs_license_migration( MY__WC_DOWNLOAD_ID, From 1deafc2b7c6ff1ce5af67659d37ff59c65876475 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Sun, 20 Nov 2016 23:00:32 +0530 Subject: [PATCH 18/22] User's email not read for migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and… `get_local_license_id` now uses download `permission_id` --- .../wc/class-fs-wc-migration-endpoint.php | 56 +++++++++++++------ .../migration/wc/class-fs-wc-migration.php | 21 ++++++- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-migration-endpoint.php b/includes/migration/wc/class-fs-wc-migration-endpoint.php index 464fd32..75d5e6d 100644 --- a/includes/migration/wc/class-fs-wc-migration-endpoint.php +++ b/includes/migration/wc/class-fs-wc-migration-endpoint.php @@ -38,7 +38,7 @@ public function __get( $name ) { return $this->_order; break; default: - $this->get_param( $name ); + return $this->get_param( $name ); } return null; @@ -98,23 +98,24 @@ public function maybe_test_full_migration() { require_once WP_FSM__DIR_INCLUDES . '/class-fs-endpoint-exception.php'; $url = 'http://wp/sfpfs/usr'; - $email = 'theguy@example.com'; - $license_key = 'wc_order_58244d828095e_am_pLl1UmGbD0eb'; + $email = 'shramee.srivastav@gmail.com'; + $license_key = 'wc_order_58303fc801265_am_S5AGJXlCCzbf'; $params = array( - "license_key" => $license_key, - "site_url" => $url, - "url" => $url, - "plugin_version" => '2.5.0', - "is_premium" => true, - "site_uid" => 'MoouenVlGG45', - "site_name" => 'GetFrappe', - "language" => 'en-US', - "charset" => 'UTF-8', - "platform_version" => '4.6.1', - "php_version" => '7.0.0', - "module_id" => 'Storefront Pro', - "email" => $email, + 'license_key' => $license_key, + 'site_url' => $url, + 'url' => $url, + 'plugin_version' => '2.5.0', + 'is_premium' => true, + 'site_uid' => '19ec32f60ec07ef9fae025ce5ac6bb86', // FS site uid + 'site_name' => 'GetFrappe', + 'wcam_site_id' => 'rAnDoM12', + 'language' => 'en-US', + 'charset' => 'UTF-8', + 'platform_version' => '4.6.1', + 'php_version' => '7.0.0', + 'module_title' => 'Storefront Pro', + //'email' => $email, 'is_active' => true, ); @@ -281,14 +282,17 @@ protected function migrate_install_license() { */ protected function validate_params() { // Software title ( which may not be same as product title ) is plugin identifier in WCAM - $plugin_title = $this->get_param( 'module_id' ); + $plugin_title = $this->get_param( 'module_title' ); + + if ( ! $this->get_param( 'email' ) ) { + $this->_request_data['email'] = $this->query_email_for_key(); + } // Get order $order_data = WCAM()->helpers->get_order_info_by_email_with_order_key( $this->get_param( 'email' ), $this->get_param( 'license_key' ) ); - // $order_data['product_id'] = WCAM()->helpers->get_product_id_by_software_title( $plugin_title, $order_data['order_id'] ); @@ -304,6 +308,22 @@ protected function validate_params() { $this->_order = (object) $order_data; } + + private function query_email_for_key() { + global $wpdb; + + $sql = " + SELECT user_email + FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions + WHERE order_key = %s + "; + + $args = explode( '_am_', $this->get_param('license_key') ); + + // Returns an Object + return $wpdb->get_var( $wpdb->prepare( $sql, $args ) ); + } + #endregion } diff --git a/includes/migration/wc/class-fs-wc-migration.php b/includes/migration/wc/class-fs-wc-migration.php index 589600a..2f7eb6d 100644 --- a/includes/migration/wc/class-fs-wc-migration.php +++ b/includes/migration/wc/class-fs-wc-migration.php @@ -193,7 +193,24 @@ protected function get_local_module_id() { * @return string */ protected function get_local_license_id() { - return $this->ep->order->order_id; + + global $wpdb; + + $sql = " + SELECT permission_id + FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions + WHERE user_email = %s + AND order_key = %s + AND product_id = %s"; + + $args = array( + $this->ep->order->license_email, + $this->ep->order->order_key, + $this->ep->order->product_id, + ); + + // Returns an Object + return $wpdb->get_var( $wpdb->prepare( $sql, $args ) ); } /** @@ -212,7 +229,7 @@ protected function get_local_install_id() { * Limit the ID to 32 chars since the entity mapping * local_id column is limited to 32 chars. */ - return $this->ep->site_uid; + return $this->ep->wcam_site_id; } /** From 591318dc791aa04436dfccb390869a30532584f6 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Fri, 25 Nov 2016 10:13:34 +0530 Subject: [PATCH 19/22] Fixing local pricing id, billing cycle and license quota --- .../migration/wc/class-fs-wc-migration.php | 47 +++---------------- 1 file changed, 6 insertions(+), 41 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-migration.php b/includes/migration/wc/class-fs-wc-migration.php index 2f7eb6d..5b89339 100644 --- a/includes/migration/wc/class-fs-wc-migration.php +++ b/includes/migration/wc/class-fs-wc-migration.php @@ -94,7 +94,7 @@ private function load_edd_entities() { return; // Subscriptions not implemented - $this->_wc_subscription = $this->get_edd_subscription( + $this->_wc_subscription = $this->get_wc_subscription( $download_id, $initial_payment_id ); @@ -126,7 +126,7 @@ private function load_edd_entities() { * * @return \WC_Subscription|false */ - private function get_edd_subscription( $download_id, $parent_payment_id ) { + private function get_wc_subscription( $download_id, $parent_payment_id ) { throw new Exception( 'Not implemented' ); if ( ! class_exists( 'WC_Recurring' ) ) { @@ -291,43 +291,6 @@ protected function get_local_billing_id() { * @return string */ protected function get_local_pricing_id() { - $price_id = 0; - - if ( $this->_product->is_type( 'variable' ) ) { - - $price_id = (int) self::$_edd_sl->get_price_id( $this->_edd_license->ID ); - - if ( 0 === $price_id ) { - /** - * Couldn't find matching price ID which means it's a legacy license. - * - * Fetch the price ID that has the same license activations quota. - */ - $edd_prices = $this->_product->get_prices(); - - $edd_license_activations_limit = self::$_edd_sl->get_license_limit( $this->_product->ID, - $this->_edd_license->ID ); - - $price_found = false; - foreach ( $edd_prices as $id => $edd_price ) { - if ( $edd_license_activations_limit == $edd_price['license_limit'] ) { - $price_id = $id; - $price_found = true; - break; - } - } - - if ( ! $price_found ) { - /** - * If license limit isn't matching any of the prices, use the first - * price ID. - */ - reset( $edd_prices ); - $price_id = key( $edd_prices ); - } - } - } - return $this->ep->order->parent_product_id . ':' . $this->ep->order->product_id; } @@ -572,6 +535,9 @@ private function get_local_subscription_gateway() { * @return int */ private function get_local_billing_cycle_in_months() { + // @todo Adjust to work with WC Subscriptions plugin. For now, assume annual plans. + return 12; + switch ( $this->_wc_subscription->period ) { case 'day': case 'week': @@ -666,8 +632,7 @@ private function get_edd_canonized_site_home_url( $url = '' ) { * @return int|null */ private function get_license_quota() { - $quota = (int) $this->ep->order->_api_activations_parent; - + $quota = (int) $this->ep->order->_api_activations; return ( $quota > 0 ) ? $quota : null; } From 7354ce0e2a1305509756f9dabae7d8687bef2da1 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Fri, 25 Nov 2016 10:58:15 +0530 Subject: [PATCH 20/22] Get correct ID even if products have same Software Title and fixing `FS_WC_Migration_Endpoint::get_local_paid_plan_id()` --- includes/migration/wc/class-fs-wc-migration-endpoint.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-migration-endpoint.php b/includes/migration/wc/class-fs-wc-migration-endpoint.php index 75d5e6d..8479ed2 100644 --- a/includes/migration/wc/class-fs-wc-migration-endpoint.php +++ b/includes/migration/wc/class-fs-wc-migration-endpoint.php @@ -99,7 +99,7 @@ public function maybe_test_full_migration() { $url = 'http://wp/sfpfs/usr'; $email = 'shramee.srivastav@gmail.com'; - $license_key = 'wc_order_58303fc801265_am_S5AGJXlCCzbf'; + $license_key = 'wc_order_583468387d63c_am_DKpGZTJBJDVH'; $params = array( 'license_key' => $license_key, @@ -140,7 +140,7 @@ public function maybe_test_full_migration() { */ protected function get_local_paid_plan_id( $product_id ) { $product = wc_get_product( $product_id ); - return $product_id . ':' . ( $product->is_type( 'variable' ) ? '1' : '0' ); + return $product->get_parent() . ':' . $product_id; } /** @@ -293,9 +293,8 @@ protected function validate_params() { $this->get_param( 'email' ), $this->get_param( 'license_key' ) ); - $order_data['product_id'] = WCAM()->helpers->get_product_id_by_software_title( - $plugin_title, $order_data['order_id'] - ); + $order_data['product_id'] = empty( $order_data['variable_product_id'] ) ? + $order_data['parent_product_id'] : $order_data['variable_product_id']; // Before checking license with WC, make sure module is synced. if ( ! $order_data['product_id'] ){ From c443ab8d2b22dc463ac42f52b6e6289604d1eff5 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Fri, 25 Nov 2016 15:14:21 +0530 Subject: [PATCH 21/22] Email not used in API --- includes/migration/wc/class-fs-wc-migration-endpoint.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-migration-endpoint.php b/includes/migration/wc/class-fs-wc-migration-endpoint.php index 8479ed2..9868672 100644 --- a/includes/migration/wc/class-fs-wc-migration-endpoint.php +++ b/includes/migration/wc/class-fs-wc-migration-endpoint.php @@ -98,8 +98,7 @@ public function maybe_test_full_migration() { require_once WP_FSM__DIR_INCLUDES . '/class-fs-endpoint-exception.php'; $url = 'http://wp/sfpfs/usr'; - $email = 'shramee.srivastav@gmail.com'; - $license_key = 'wc_order_583468387d63c_am_DKpGZTJBJDVH'; + $license_key = 'wc_order_5645a84f75f0f_am_kyidNs5CzIlu'; $params = array( 'license_key' => $license_key, @@ -115,7 +114,6 @@ public function maybe_test_full_migration() { 'platform_version' => '4.6.1', 'php_version' => '7.0.0', 'module_title' => 'Storefront Pro', - //'email' => $email, 'is_active' => true, ); From 2a6ea37008b3e1cc30c8211453a0bc9ce2ce92e0 Mon Sep 17 00:00:00 2001 From: Shramee Srivastav Date: Tue, 29 Nov 2016 17:13:32 +0530 Subject: [PATCH 22/22] Unknown payment method error should be either `paypal` or `cc` --- includes/migration/wc/class-fs-wc-migration.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/migration/wc/class-fs-wc-migration.php b/includes/migration/wc/class-fs-wc-migration.php index 5b89339..80d5c65 100644 --- a/includes/migration/wc/class-fs-wc-migration.php +++ b/includes/migration/wc/class-fs-wc-migration.php @@ -747,7 +747,6 @@ protected function get_purchase_vat_for_api() { protected function get_purchase_for_api() { $purchase = array(); $purchase['billing_cycle'] = 0; - $purchase['payment_method'] = $this->_wc_order->payment_method; $purchase['customer_external_id'] = 'wc_customer_' . $this->ep->order->user_id; $purchase['license_key'] = substr( $this->ep->order->api_key, 6 ); $purchase['license_quota'] = $this->get_license_quota(); // Preserve license activations limit. @@ -760,7 +759,9 @@ protected function get_purchase_for_api() { $purchase['license_expires_at'] = $license_expiration; } - if ( ! $purchase['payment_method'] ) { + if ( false !== strpos( strtolower( $this->_wc_order->payment_method ), 'paypal' ) ) { + $purchase['payment_method'] = 'paypal'; + } else { $purchase['payment_method'] = 'cc'; }