/**
  * Completely whipe all Membership data from Database.
  *
  * Note: This function is not used currently...
  *
  * @since  1.0.0
  */
 private static function cleanup_db()
 {
     global $wpdb;
     $sql = array();
     $trash_ids = array();
     // Delete membership meta-data from users.
     $users = MS_Model_Member::get_members();
     foreach ($users as $user) {
         $user->delete_all_membership_usermeta();
         $user->save();
     }
     // Determine IDs of Membership Pages.
     $page_types = MS_Model_Pages::get_page_types();
     foreach ($page_types as $type => $name) {
         $page_id = MS_Model_Pages::get_setting($type);
         $trash_ids[] = $page_id;
     }
     /**
      * Delete all plugin settings.
      * Settings are saved by classes that extend MS_Model_option
      */
     foreach (MS_Model_Gateway::get_gateways() as $option) {
         $option->delete();
     }
     MS_Factory::load('MS_Model_Addon')->delete();
     MS_Factory::load('MS_Model_Pages')->delete();
     MS_Factory::load('MS_Model_Settings')->delete();
     /**
      * Delete transient data
      * Transient data is saved by classed that extend MS_Model_Transient
      */
     MS_Factory::load('MS_Model_Simulate')->delete();
     /**
      * Delete all plugin content.
      * Content is saved by classes that extend MS_Model_CustomPostType
      */
     $ms_posttypes = array(MS_Model_Communication::get_post_type(), MS_Model_Event::get_post_type(), MS_Model_Invoice::get_post_type(), MS_Model_Transactionlog::get_post_type(), MS_Model_Membership::get_post_type(), MS_Model_Relationship::get_post_type(), MS_Addon_Coupon_Model::get_post_type(), MS_Addon_Invitation_Model::get_post_type());
     foreach ($ms_posttypes as $type) {
         $sql[] = $wpdb->prepare("DELETE FROM {$wpdb->posts} WHERE post_type = %s;", $type);
     }
     // Remove orphaned post-metadata.
     $sql[] = "\n\t\tDELETE FROM {$wpdb->postmeta}\n\t\tWHERE NOT EXISTS (\n\t\t\tSELECT 1 FROM {$wpdb->posts} tmp WHERE tmp.ID = post_id\n\t\t);\n\t\t";
     // Clear all WP transient cache.
     $sql[] = "\n\t\tDELETE FROM {$wpdb->options}\n\t\tWHERE option_name LIKE '_transient_%';\n\t\t";
     foreach ($sql as $s) {
         $wpdb->query($s);
     }
     // Move Membership pages to trash.
     foreach ($trash_ids as $id) {
         wp_delete_post($id, true);
     }
     // Clear all data from WP Object cache.
     wp_cache_flush();
     // Redirect to the main page.
     wp_safe_redirect(MS_Controller_Plugin::get_admin_url());
     exit;
 }
 /**
  * Defines predefines filters for this list table.
  *
  * @since  1.0.0
  * @return array
  */
 public function get_views()
 {
     $views = array();
     $base_url = remove_query_arg(array('state', 'id', 'invoice'));
     $views['all'] = array('label' => __('All', 'membership2'), 'url' => $base_url, 'count' => MS_Model_Transactionlog::get_item_count());
     $views['ok'] = array('label' => __('Successful', 'membership2'), 'url' => add_query_arg('state', 'ok', $base_url), 'count' => MS_Model_Transactionlog::get_item_count(array('state' => 'ok')));
     $views['err'] = array('label' => __('Failed', 'membership2'), 'url' => add_query_arg('state', 'err', $base_url), 'count' => MS_Model_Transactionlog::get_item_count(array('state' => 'err')));
     $views['ignore'] = array('label' => __('Ignored', 'membership2'), 'url' => add_query_arg('state', 'ignore', $base_url), 'count' => MS_Model_Transactionlog::get_item_count(array('state' => 'ignore')));
     return apply_filters('ms_helper_listtable_transactionlog_views', $views);
 }
 /**
  * Processes gateway IPN return.
  *
  * @since  1.0.0
  * @param  MS_Model_Transactionlog $log Optional. A transaction log item
  *         that will be updated instead of creating a new log entry.
  */
 public function handle_return($log = false)
 {
     $success = false;
     $exit = false;
     $redirect = false;
     $notes = '';
     $status = null;
     $external_id = null;
     $invoice_id = 0;
     $subscription_id = 0;
     $amount = 0;
     if (!empty($_POST['vendor_order_id']) && !empty($_POST['md5_hash'])) {
         $invoice_id = intval($_POST['vendor_order_id']);
         $invoice = MS_Factory::load('MS_Model_Invoice', $invoice_id);
         $raw_hash = $_POST['sale_id'] . $this->seller_id . $_POST['invoice_id'] . $this->secret_word;
         $md5_hash = strtoupper(md5($raw_hash));
         if ($md5_hash == $_POST['md5_hash'] && !empty($_POST['message_type']) && ($invoice->id = $invoice_id)) {
             $subscription = $invoice->get_subscription();
             $membership = $subscription->get_membership();
             $member = $subscription->get_member();
             $subscription_id = $subscription->id;
             $external_id = $_POST['invoice_id'];
             $amount = (double) $_POST['invoice_list_amount'];
             switch ($_POST['message_type']) {
                 case 'RECURRING_INSTALLMENT_SUCCESS':
                     $notes = 'Payment received';
                     // Not sure if the invoice was already paid via the
                     // INVOICE_STATUS_CHANGED message
                     if (!$invoice->is_paid()) {
                         $success = true;
                     }
                     break;
                 case 'INVOICE_STATUS_CHANGED':
                     $notes = sprintf('Invoice was %s', $_POST['invoice_status']);
                     switch ($_POST['invoice_status']) {
                         case 'deposited':
                             // Not sure if invoice was already paid via the
                             // RECURRING_INSTALLMENT_SUCCESS message.
                             if (!$invoice->is_paid()) {
                                 $success = true;
                             }
                             break;
                         case 'declied':
                             $status = MS_Model_Invoice::STATUS_DENIED;
                             break;
                     }
                     break;
                 case 'RECURRING_STOPPED':
                     $notes = 'Recurring payments stopped manually';
                     $member->cancel_membership($membership->id);
                     $member->save();
                     break;
                 case 'FRAUD_STATUS_CHANGED':
                     $notes = 'Ignored: Users Fraud-status was checked';
                     $success = null;
                     break;
                 case 'ORDER_CREATED':
                     $notes = 'Ignored: 2Checkout created a new order';
                     $success = null;
                     break;
                 case 'RECURRING_RESTARTED':
                     $notes = 'Ignored: Recurring payments started';
                     $success = null;
                     break;
                 case 'RECURRING_COMPLETE':
                     $notes = 'Ignored: Recurring complete';
                     $success = null;
                     break;
                 case 'RECURRING_INSTALLMENT_FAILED':
                     $notes = 'Ignored: Recurring payment failed';
                     $success = null;
                     $status = MS_Model_Invoice::STATUS_PENDING;
                     break;
                 default:
                     $notes = sprintf('Warning: Unclear command "%s"', $_POST['message_type']);
                     break;
             }
             $invoice->add_notes($notes);
             $invoice->save();
             if ($success) {
                 $invoice->pay_it($this->id, $external_id);
             } elseif (!empty($status)) {
                 $invoice->status = $status;
                 $invoice->save();
                 $invoice->changed();
             }
             do_action('ms_gateway_2checkout_payment_processed_' . $status, $invoice, $subscription);
         } else {
             $reason = 'Unexpected transaction response';
             switch (true) {
                 case $md5_hash != $_POST['md5_hash']:
                     $reason = 'MD5 Hash invalid';
                     break;
                 case empty($_POST['message_type']):
                     $reason = 'Message type is empty';
                     break;
                 case $invoice->id != $invoice_id:
                     $reason = sprintf('Expected invoice_id "%s" but got "%s"', $invoice->id, $invoice_id);
                     break;
             }
             $notes = 'Response Error: ' . $reason;
             MS_Helper_Debug::log($notes);
             MS_Helper_Debug::log($response);
             $exit = true;
         }
     } else {
         // Did not find expected POST variables. Possible access attempt from a non PayPal site.
         $notes = 'Error: Missing POST variables. Identification is not possible.';
         MS_Helper_Debug::log($notes);
         $redirect = MS_Helper_Utility::home_url('/');
         $exit = true;
     }
     if (!$log) {
         do_action('ms_gateway_transaction_log', self::ID, 'handle', $success, $subscription_id, $invoice_id, $amount, $notes, $external_id);
         if ($redirect) {
             wp_safe_redirect($redirect);
             exit;
         }
         if ($exit) {
             exit;
         }
     } else {
         $log->invoice_id = $invoice_id;
         $log->subscription_id = $subscription_id;
         $log->amount = $amount;
         $log->description = $notes;
         $log->external_id = $external_id;
         if ($success) {
             $log->manual_state('ok');
         }
         $log->save();
     }
     do_action('ms_gateway_2checkout_handle_return_after', $this);
     if ($log) {
         return $log;
     }
 }
 /**
  * Processes gateway IPN return.
  *
  * @since  1.0.0
  * @param  MS_Model_Transactionlog $log Optional. A transaction log item
  *         that will be updated instead of creating a new log entry.
  */
 public function handle_return($log = false)
 {
     $success = false;
     $exit = false;
     $redirect = false;
     $notes = '';
     $status = null;
     $invoice_id = 0;
     $subscription_id = 0;
     $amount = 0;
     do_action('ms_gateway_paypalsingle_handle_return_before', $this);
     lib3()->array->strip_slashes($_POST, 'pending_reason');
     if ((isset($_POST['payment_status']) || isset($_POST['txn_type'])) && !empty($_POST['invoice'])) {
         if ($this->is_live_mode()) {
             $domain = 'https://www.paypal.com';
         } else {
             $domain = 'https://www.sandbox.paypal.com';
         }
         // Ask PayPal to validate our $_POST data.
         $ipn_data = (array) stripslashes_deep($_POST);
         $ipn_data['cmd'] = '_notify-validate';
         $response = wp_remote_post($domain . '/cgi-bin/webscr', array('timeout' => 60, 'sslverify' => false, 'httpversion' => '1.1', 'body' => $ipn_data));
         $invoice_id = intval($_POST['invoice']);
         $external_id = $_POST['txn_id'];
         $amount = (double) $_POST['mc_gross'];
         $currency = $_POST['mc_currency'];
         $invoice = MS_Factory::load('MS_Model_Invoice', $invoice_id);
         if (!is_wp_error($response) && !MS_Model_Transactionlog::was_processed(self::ID, $external_id) && 200 == $response['response']['code'] && !empty($response['body']) && 'VERIFIED' == $response['body'] && $invoice->id == $invoice_id) {
             $new_status = false;
             $subscription = $invoice->get_subscription();
             $membership = $subscription->get_membership();
             $member = $subscription->get_member();
             $subscription_id = $subscription->id;
             // Process PayPal response
             switch ($_POST['payment_status']) {
                 // Successful payment
                 case 'Completed':
                 case 'Processed':
                     $success = true;
                     if ($amount == $invoice->total) {
                         $notes .= __('Payment successful', 'membership2');
                     } else {
                         $notes .= __('Payment registered, though amount differs from invoice.', 'membership2');
                     }
                     break;
                 case 'Reversed':
                     $notes = __('Last transaction has been reversed. Reason: Payment has been reversed (charge back). ', 'membership2');
                     $status = MS_Model_Invoice::STATUS_DENIED;
                     break;
                 case 'Refunded':
                     $notes = __('Last transaction has been reversed. Reason: Payment has been refunded', 'membership2');
                     $status = MS_Model_Invoice::STATUS_DENIED;
                     break;
                 case 'Denied':
                     $notes = __('Last transaction has been reversed. Reason: Payment Denied', 'membership2');
                     $status = MS_Model_Invoice::STATUS_DENIED;
                     break;
                 case 'Pending':
                     $pending_str = array('address' => __('Customer did not include a confirmed shipping address', 'membership2'), 'authorization' => __('Funds not captured yet', 'membership2'), 'echeck' => __('eCheck that has not cleared yet', 'membership2'), 'intl' => __('Payment waiting for aproval by service provider', 'membership2'), 'multi-currency' => __('Payment waiting for service provider to handle multi-currency process', 'membership2'), 'unilateral' => __('Customer did not register or confirm his/her email yet', 'membership2'), 'upgrade' => __('Waiting for service provider to upgrade the PayPal account', 'membership2'), 'verify' => __('Waiting for service provider to verify his/her PayPal account', 'membership2'), '*' => '');
                     $reason = $_POST['pending_reason'];
                     $notes = __('Last transaction is pending. Reason: ', 'membership2') . (isset($pending_str[$reason]) ? $pending_str[$reason] : $pending_str['*']);
                     $status = MS_Model_Invoice::STATUS_PENDING;
                     break;
                 default:
                 case 'Partially-Refunded':
                 case 'In-Progress':
                     $success = null;
                     break;
             }
             if ('new_case' == $_POST['txn_type'] && 'dispute' == $_POST['case_type']) {
                 // Status: Dispute
                 $status = MS_Model_Invoice::STATUS_DENIED;
                 $notes = __('Dispute about this payment', 'membership2');
             }
             if (!empty($notes)) {
                 $invoice->add_notes($notes);
             }
             $invoice->save();
             if ($success) {
                 $invoice->pay_it($this->id, $external_id);
             } elseif (!empty($status)) {
                 $invoice->status = $status;
                 $invoice->save();
                 $invoice->changed();
             }
             do_action('ms_gateway_paypalsingle_payment_processed_' . $status, $invoice, $subscription);
         } else {
             $reason = 'Unexpected transaction response';
             switch (true) {
                 case is_wp_error($response):
                     $reason = 'Response is error';
                     break;
                 case 200 != $response['response']['code']:
                     $reason = 'Response code is ' . $response['response']['code'];
                     break;
                 case empty($response['body']):
                     $reason = 'Response is empty';
                     break;
                 case 'VERIFIED' != $response['body']:
                     $reason = sprintf('Expected response "%s" but got "%s"', 'VERIFIED', (string) $response['body']);
                     break;
                 case $invoice->id != $invoice_id:
                     $reason = sprintf('Expected invoice_id "%s" but got "%s"', $invoice->id, $invoice_id);
                     break;
                 case MS_Model_Transactionlog::was_processed(self::ID, $external_id):
                     $reason = 'Duplicate: Already processed that transaction.';
                     break;
             }
             $notes = 'Response Error: ' . $reason;
             $exit = true;
         }
     } else {
         // Did not find expected POST variables. Possible access attempt from a non PayPal site.
         $u_agent = $_SERVER['HTTP_USER_AGENT'];
         if (false === strpos($u_agent, 'PayPal')) {
             // Very likely someone tried to open the URL manually. Redirect to home page
             $notes = 'Error: Missing POST variables. Redirect user to Home-URL.';
             $redirect = MS_Helper_Utility::home_url('/');
         } else {
             $notes = 'Error: Missing POST variables. Identification is not possible.';
         }
         $exit = true;
     }
     if (!$log) {
         do_action('ms_gateway_transaction_log', self::ID, 'handle', $success, $subscription_id, $invoice_id, $amount, $notes, $external_id);
         if ($redirect) {
             wp_safe_redirect($redirect);
             exit;
         }
         if ($exit) {
             exit;
         }
     } else {
         $log->invoice_id = $invoice_id;
         $log->subscription_id = $subscription_id;
         $log->amount = $amount;
         $log->description = $notes;
         $log->external_id = $external_id;
         if ($success) {
             $log->manual_state('ok');
         }
         $log->save();
     }
     do_action('ms_gateway_paypalsingle_handle_return_after', $this, $log);
     if ($log) {
         return $log;
     }
 }
 /**
  * Loads the items that are displayed on the current list page.
  *
  * @since  1.0.1.2
  */
 public function prepare_items()
 {
     $this->_column_headers = array($this->get_columns(), $this->get_hidden_columns(), $this->get_sortable_columns());
     $current_page = $this->get_pagenum();
     $args = array('posts_per_page' => -1, 'offset' => 0);
     if (!empty($_GET['source']) && !empty($_GET['source_id'])) {
         $this->matching_type = $_GET['source'];
         $this->matching_type_id = $_GET['source_id'];
         $args['state'] = array('err', 'ignore');
         $args['source'] = array($this->matching_type_id, $this->matching_type);
         $total_items = MS_Model_Transactionlog::get_item_count($args);
         $this->items = apply_filters('ms_helper_listtable_transactionmatching_items', MS_Model_Transactionlog::get_items($args));
         $this->set_pagination_args(array('total_items' => $total_items, 'per_page' => $per_page));
     }
 }
    /**
     * Create view output.
     *
     * @since  1.0.0
     *
     * @return string
     */
    public function to_html()
    {
        $this->check_simulation();
        $buttons = array();
        // Count invalid transactions.
        $args = array('state' => 'err');
        $error_count = MS_Model_Transactionlog::get_item_count($args);
        if ($error_count && (empty($_GET['state']) || 'err' != $_GET['state'])) {
            if (1 == $error_count) {
                $message = __('One transaction failed. Please %2$sreview the logs%3$s and decide if you want to ignore the transaction or manually assign it to an invoice.', MS_TEXT_DOMAIN);
            } else {
                $message = __('%1$s transactions failed. Please %2$sreview the logs%3$s and decide if you want to ignore the transaction or manually assign it to an invoice.', MS_TEXT_DOMAIN);
            }
            $review_url = MS_Controller_Plugin::get_admin_url('billing', array('show' => 'logs', 'state' => 'err'));
            lib2()->ui->admin_message(sprintf($message, $error_count, '<a href="' . $review_url . '">', '</a>'), 'err');
        }
        if (isset($_GET['show']) && 'logs' == $_GET['show']) {
            $title = __('Transaction Logs', MS_TEXT_DOMAIN);
            $listview = MS_Factory::create('MS_Helper_ListTable_TransactionLog');
            $listview->prepare_items();
            $buttons[] = array('type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing'), 'value' => __('Show Invoices', MS_TEXT_DOMAIN), 'class' => 'button');
        } else {
            $title = __('Billing', MS_TEXT_DOMAIN);
            $listview = MS_Factory::create('MS_Helper_ListTable_Billing');
            $listview->prepare_items();
            $buttons[] = array('id' => 'add_new', 'type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing', array('action' => MS_Controller_Billing::ACTION_EDIT, 'invoice_id' => 0)), 'value' => __('Create new Invoice', MS_TEXT_DOMAIN), 'class' => 'button');
            $buttons[] = array('type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing', array('show' => 'logs')), 'value' => __('Show Transaction Logs', MS_TEXT_DOMAIN), 'class' => 'button');
            if (!empty($_GET['gateway_id'])) {
                $gateway = MS_Model_Gateway::factory($_GET['gateway_id']);
                if ($gateway->name) {
                    $title .= ' - ' . $gateway->name;
                }
            }
        }
        ob_start();
        ?>

		<div class="wrap ms-wrap ms-billing">
			<?php 
        MS_Helper_Html::settings_header(array('title' => $title, 'title_icon_class' => 'wpmui-fa wpmui-fa-credit-card'));
        ?>
			<div>
				<?php 
        foreach ($buttons as $button) {
            MS_Helper_Html::html_element($button);
        }
        ?>
			</div>
			<?php 
        $listview->views();
        $listview->search_box(__('User', MS_TEXT_DOMAIN), 'search');
        ?>
			<form action="" method="post">
				<?php 
        $listview->display();
        ?>
			</form>
		</div>

		<?php 
        $html = ob_get_clean();
        return apply_filters('ms_view_billing_list', $html, $this);
    }
 /**
  * Loads the items that are displayed on the current list page.
  *
  * @since  1.0.0
  */
 public function prepare_items()
 {
     $this->_column_headers = array($this->get_columns(), $this->get_hidden_columns(), $this->get_sortable_columns());
     $per_page = $this->get_items_per_page('transactionlog_per_page', self::DEFAULT_PAGE_SIZE);
     $current_page = $this->get_pagenum();
     $args = array('posts_per_page' => $per_page, 'offset' => ($current_page - 1) * $per_page);
     if (!empty($_GET['state'])) {
         $args['state'] = $_GET['state'];
     }
     if (!empty($_GET['gateway_id'])) {
         $args['meta_query'] = array('gateway_id' => array('key' => '_gateway_id', 'value' => $_GET['gateway_id']));
     }
     $total_items = MS_Model_Transactionlog::get_item_count($args);
     $this->items = apply_filters('ms_helper_listtable_transactionlog_items', MS_Model_Transactionlog::get_items($args));
     $this->set_pagination_args(array('total_items' => $total_items, 'per_page' => $per_page));
 }
    /**
     * Create view output.
     *
     * @since  1.0.0
     *
     * @return string
     */
    public function to_html()
    {
        $this->check_simulation();
        $buttons = array();
        $module = 'billing';
        if (isset($_GET['show'])) {
            $module = $_GET['show'];
        }
        if (!$module) {
            // Show a message if there are error-state transactions.
            $args = array('state' => 'err');
            $error_count = MS_Model_Transactionlog::get_item_count($args);
            if ($error_count) {
                if (1 == $error_count) {
                    $message = __('One transaction failed. Please %2$sreview the logs%3$s and decide if you want to ignore the transaction or manually assign it to an invoice.', 'membership2');
                } else {
                    $message = __('%1$s transactions failed. Please %2$sreview the logs%3$s and decide if you want to ignore the transaction or manually assign it to an invoice.', 'membership2');
                }
                $review_url = MS_Controller_Plugin::get_admin_url('billing', array('show' => 'logs', 'state' => 'err'));
                lib3()->ui->admin_message(sprintf($message, $error_count, '<a href="' . $review_url . '">', '</a>'), 'err');
            }
        }
        // Decide which list to display in the Billings page.
        switch ($module) {
            // Transaction logs.
            case 'logs':
                $title = __('Transaction Logs', 'membership2');
                $listview = MS_Factory::create('MS_Helper_ListTable_TransactionLog');
                $listview->prepare_items();
                $buttons[] = array('type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing'), 'value' => __('Show Invoices', 'membership2'), 'class' => 'button');
                break;
                // M1 Migration matching.
            // M1 Migration matching.
            case 'matching':
                $title = __('Automatic Transaction Matching', 'membership2');
                $listview = MS_Factory::create('MS_Helper_ListTable_TransactionMatching');
                $listview->prepare_items();
                $buttons[] = array('type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing'), 'value' => __('Show Invoices', 'membership2'), 'class' => 'button');
                $buttons[] = array('type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing', array('show' => 'logs')), 'value' => __('Show Transaction Logs', 'membership2'), 'class' => 'button');
                break;
                // Default billings list.
            // Default billings list.
            case 'billing':
            default:
                $title = __('Billing', 'membership2');
                $listview = MS_Factory::create('MS_Helper_ListTable_Billing');
                $listview->prepare_items();
                $buttons[] = array('id' => 'add_new', 'type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing', array('action' => MS_Controller_Billing::ACTION_EDIT, 'invoice_id' => 0)), 'value' => __('Create new Invoice', 'membership2'), 'class' => 'button');
                $buttons[] = array('type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing', array('show' => 'logs')), 'value' => __('Show Transaction Logs', 'membership2'), 'class' => 'button');
                if (!empty($_GET['gateway_id'])) {
                    $gateway = MS_Model_Gateway::factory($_GET['gateway_id']);
                    if ($gateway->name) {
                        $title .= ' - ' . $gateway->name;
                    }
                }
                break;
        }
        if ('matching' != $module) {
            if (MS_Model_Import::can_match()) {
                $btn_label = __('Setup automatic matching', 'membership2');
                $btn_class = 'button';
            } else {
                $btn_label = '(' . __('Setup automatic matching', 'membership2') . ')';
                $btn_class = 'button button-link';
            }
            $buttons[] = array('type' => MS_Helper_Html::TYPE_HTML_LINK, 'url' => MS_Controller_Plugin::get_admin_url('billing', array('show' => 'matching')), 'value' => $btn_label, 'class' => $btn_class);
        }
        // Default list view part - dislay prepared values from above.
        ob_start();
        ?>

		<div class="wrap ms-wrap ms-billing">
			<?php 
        MS_Helper_Html::settings_header(array('title' => $title, 'title_icon_class' => 'wpmui-fa wpmui-fa-credit-card'));
        ?>
			<div>
				<?php 
        foreach ($buttons as $button) {
            MS_Helper_Html::html_element($button);
        }
        ?>
			</div>
			<?php 
        $listview->views();
        $listview->search_box(__('User', 'membership2'), 'search');
        ?>
			<form action="" method="post">
				<?php 
        $listview->display();
        ?>
			</form>
		</div>

		<?php 
        $html = ob_get_clean();
        return apply_filters('ms_view_billing_list', $html, $this);
    }
 /**
  * Processes gateway IPN return.
  *
  * @since  1.0.0
  * @param  MS_Model_Transactionlog $log Optional. A transaction log item
  *         that will be updated instead of creating a new log entry.
  */
 public function handle_return($log = false)
 {
     $success = false;
     $ignore = false;
     $exit = false;
     $redirect = false;
     $notes = '';
     $status = null;
     $notes_pay = '';
     $notes_txn = '';
     $external_id = null;
     $invoice_id = 0;
     $subscription_id = 0;
     $amount = 0;
     $transaction_type = '';
     $payment_status = '';
     $ext_type = false;
     if (!empty($_POST['txn_type'])) {
         $transaction_type = strtolower($_POST['txn_type']);
     }
     if (isset($_POST['mc_gross'])) {
         $amount = (double) $_POST['mc_gross'];
     } elseif (isset($_POST['mc_amount3'])) {
         // mc_amount1 and mc_amount2 are for trial period prices.
         $amount = (double) $_POST['mc_amount3'];
     }
     if (!empty($_POST['payment_status'])) {
         $payment_status = strtolower($_POST['payment_status']);
     }
     if (!empty($_POST['txn_id'])) {
         $external_id = $_POST['txn_id'];
     }
     if (!empty($_POST['mc_currency'])) {
         $currency = $_POST['mc_currency'];
     }
     // Step 1: Find the invoice_id and determine if payment is M2 or M1.
     if ($payment_status || $transaction_type) {
         if (!empty($_POST['invoice'])) {
             // BEST CASE:
             // 'invoice' is set in all regular M2 subscriptions!
             $invoice_id = intval($_POST['invoice']);
             /*
              * PayPal only knows the first invoice of the subscription.
              * So we need to check: If the invoice is already paid then the
              * payment is for a follow-up invoice.
              */
             $invoice = MS_Factory::load('MS_Model_Invoice', $invoice_id);
             if ($invoice->is_paid()) {
                 $subscription = $invoice->get_subscription();
                 $invoice_id = $subscription->first_unpaid_invoice();
             }
         } elseif (!empty($_POST['custom'])) {
             // FALLBACK A:
             // Maybe it's an imported M1 subscription.
             $infos = explode(':', $_POST['custom']);
             if (count($infos) > 2) {
                 // $infos should contain [timestamp, user_id, sub_id, key]
                 $m1_user_id = intval($infos[1]);
                 $m1_sub_id = intval($infos[2]);
                 // Roughtly equals M2 membership->id.
                 // M1 payments use the following type/status values.
                 $pay_types = array('subscr_signup', 'subscr_payment');
                 $pay_stati = array('completed', 'processed');
                 if ($m1_user_id > 0 && $m1_sub_id > 0) {
                     if (in_array($transaction_type, $pay_types)) {
                         $ext_type = 'm1';
                     } elseif (in_array($payment_status, $pay_stati)) {
                         $ext_type = 'm1';
                     }
                 }
                 if ('m1' == $ext_type) {
                     $is_linked = false;
                     // Seems to be a valid M1 payment:
                     // Find the associated imported subscription!
                     $subscription = MS_Model_Import::find_subscription($m1_user_id, $m1_sub_id, 'source', self::ID);
                     if (!$subscription) {
                         $membership = MS_Model_Import::membership_by_source($m1_sub_id);
                         if ($membership) {
                             $is_linked = true;
                             $notes = sprintf('Error: User is not subscribed to Membership %s.', $membership->id);
                         }
                     }
                     $invoice_id = $subscription->first_unpaid_invoice();
                     if (!$is_linked && !$invoice_id) {
                         MS_Model_Import::need_matching($m1_sub_id, 'm1');
                     }
                 }
                 // end if: 'm1' == $ext_type
             }
         } elseif (!empty($_POST['btn_id']) && !empty($_POST['payer_email'])) {
             // FALLBACK B:
             // Payment was made by a custom PayPal Payment button.
             $user = get_user_by('email', $_POST['payer_email']);
             if ($user && $user->ID) {
                 $ext_type = 'pay_btn';
                 $is_linked = false;
                 $subscription = MS_Model_Import::find_subscription($user->ID, $_POST['btn_id'], 'pay_btn', self::ID);
                 if (!$subscription) {
                     $membership = MS_Model_Import::membership_by_matching('pay_btn', $_POST['btn_id']);
                     if ($membership) {
                         $is_linked = true;
                         $notes = sprintf('Error: User is not subscribed to Membership %s.', $membership->id);
                     }
                 }
                 $invoice_id = $subscription->first_unpaid_invoice();
                 if (!$is_linked && !$invoice_id) {
                     MS_Model_Import::need_matching($_POST['btn_id'], 'pay_btn');
                 }
             } else {
                 $notes = sprintf('Error: Could not find user "%s".', $_POST['payer_email']);
             }
             // end if: 'pay_btn' == $ext_type
         }
     }
     // Step 2a: Check if the txn_id was already processed by M2.
     if (MS_Model_Transactionlog::was_processed(self::ID, $external_id)) {
         $notes = 'Duplicate: Already processed that transaction.';
         $success = false;
         $ignore = true;
     } elseif ($invoice_id) {
         if ($this->is_live_mode()) {
             $domain = 'https://www.paypal.com';
         } else {
             $domain = 'https://www.sandbox.paypal.com';
         }
         // PayPal post authenticity verification.
         $ipn_data = (array) stripslashes_deep($_POST);
         $ipn_data['cmd'] = '_notify-validate';
         $response = wp_remote_post($domain . '/cgi-bin/webscr', array('timeout' => 60, 'sslverify' => false, 'httpversion' => '1.1', 'body' => $ipn_data));
         $invoice = MS_Factory::load('MS_Model_Invoice', $invoice_id);
         if (!is_wp_error($response) && 200 == $response['response']['code'] && !empty($response['body']) && 'VERIFIED' == $response['body'] && $invoice->id == $invoice_id) {
             $subscription = $invoice->get_subscription();
             $membership = $subscription->get_membership();
             $member = $subscription->get_member();
             $subscription_id = $subscription->id;
             // Process PayPal payment status
             if ($payment_status) {
                 switch ($payment_status) {
                     // Successful payment
                     case 'completed':
                     case 'processed':
                         $success = true;
                         if ($amount == $invoice->total) {
                             $notes .= __('Payment successful', 'membership2');
                         } else {
                             $notes .= __('Payment registered, though amount differs from invoice.', 'membership2');
                         }
                         break;
                     case 'reversed':
                         $notes_pay = __('Last transaction has been reversed. Reason: Payment has been reversed (charge back).', 'membership2');
                         $status = MS_Model_Invoice::STATUS_DENIED;
                         $ignore = true;
                         break;
                     case 'refunded':
                         $notes_pay = __('Last transaction has been reversed. Reason: Payment has been refunded.', 'membership2');
                         $status = MS_Model_Invoice::STATUS_DENIED;
                         $ignore = true;
                         break;
                     case 'denied':
                         $notes_pay = __('Last transaction has been reversed. Reason: Payment Denied.', 'membership2');
                         $status = MS_Model_Invoice::STATUS_DENIED;
                         $ignore = true;
                         break;
                     case 'pending':
                         lib3()->array->strip_slashes($_POST, 'pending_reason');
                         $notes_pay = __('Last transaction is pending.', 'membership2') . ' ';
                         switch ($_POST['pending_reason']) {
                             case 'address':
                                 $notes_pay .= __('Customer did not include a confirmed shipping address', 'membership2');
                                 break;
                             case 'authorization':
                                 $notes_pay .= __('Funds not captured yet', 'membership2');
                                 break;
                             case 'echeck':
                                 $notes_pay .= __('The eCheck has not cleared yet', 'membership2');
                                 break;
                             case 'intl':
                                 $notes_pay .= __('Payment waiting for approval by service provider', 'membership2');
                                 break;
                             case 'multi-currency':
                                 $notes_pay .= __('Payment waiting for service provider to handle multi-currency process', 'membership2');
                                 break;
                             case 'unilateral':
                                 $notes_pay .= __('Customer did not register or confirm his/her email yet', 'membership2');
                                 break;
                             case 'upgrade':
                                 $notes_pay .= __('Waiting for service provider to upgrade the PayPal account', 'membership2');
                                 break;
                             case 'verify':
                                 $notes_pay .= __('Waiting for service provider to verify his/her PayPal account', 'membership2');
                                 break;
                             default:
                                 $notes_pay .= __('Unknown reason', 'membership2');
                                 break;
                         }
                         $status = MS_Model_Invoice::STATUS_PENDING;
                         $ignore = true;
                         break;
                     default:
                     case 'partially-refunded':
                     case 'in-progress':
                         $notes_pay = sprintf(__('Not handling payment_status: %s', 'membership2'), $payment_status);
                         $ignore = true;
                         break;
                 }
             }
             // Check for subscription details
             if ($transaction_type) {
                 switch ($transaction_type) {
                     case 'subscr_signup':
                     case 'subscr_payment':
                         // Payment was received
                         $notes_txn = __('PayPal Subscripton has been created.', 'membership2');
                         if (0 == $invoice->total) {
                             $success = true;
                         } else {
                             $ignore = true;
                         }
                         break;
                     case 'subscr_modify':
                         // Payment profile was modified
                         $notes_txn = __('PayPal Subscription has been modified.', 'membership2');
                         $ignore = true;
                         break;
                     case 'recurring_payment_profile_canceled':
                     case 'subscr_cancel':
                         // Subscription was manually cancelled.
                         $notes_txn = __('PayPal Subscription has been canceled.', 'membership2');
                         $member->cancel_membership($membership->id);
                         $member->save();
                         $ignore = true;
                         break;
                     case 'recurring_payment_suspended':
                         // Recurring subscription was manually suspended.
                         $notes_txn = __('PayPal Subscription has been suspended.', 'membership2');
                         $member->cancel_membership($membership->id);
                         $member->save();
                         $ignore = true;
                         break;
                     case 'recurring_payment_suspended_due_to_max_failed_payment':
                         // Recurring subscription was automatically suspended.
                         $notes_txn = __('PayPal Subscription has failed.', 'membership2');
                         $member->cancel_membership($membership->id);
                         $member->save();
                         $ignore = true;
                         break;
                     case 'new_case':
                         // New Dispute was filed for a payment.
                         $status = MS_Model_Invoice::STATUS_DENIED;
                         $ignore = true;
                         break;
                     case 'subscr_eot':
                         /*
                          * Meaning: Subscription expired.
                          *
                          *   - after a one-time payment was made
                          *   - after last transaction in a recurring subscription
                          *   - payment failed
                          *   - ...
                          *
                          * We do not handle this event...
                          *
                          * One time payment sends 3 messages:
                          *   1. subscr_start (new subscription starts)
                          *   2. subscr_payment (payment confirmed)
                          *   3. subscr_eot (subscription ends)
                          */
                         $notes_txn = __('No more payments will be made for this subscription.', 'membership2');
                         $ignore = true;
                         break;
                     default:
                         // Other event that we do not have a case for...
                         $notes_txn = sprintf(__('Not handling txn_type: %s', 'membership2'), $transaction_type);
                         $ignore = true;
                         break;
                 }
             }
             if (!empty($notes_pay)) {
                 $invoice->add_notes($notes_pay);
             }
             if (!empty($notes_txn)) {
                 $invoice->add_notes($notes_txn);
             }
             if ($notes_pay) {
                 $notes .= ($notes ? ' | ' : '') . $notes_pay;
             }
             if ($notes_txn) {
                 $notes .= ($notes ? ' | ' : '') . $notes_txn;
             }
             $invoice->save();
             if ($success) {
                 $invoice->pay_it($this->id, $external_id);
             } elseif (!empty($status)) {
                 $invoice->status = $status;
                 $invoice->save();
                 $invoice->changed();
             }
             do_action('ms_gateway_paypalstandard_payment_processed_' . $status, $invoice, $subscription);
         } else {
             $reason = 'Unexpected transaction response';
             switch (true) {
                 case is_wp_error($response):
                     $reason = 'PayPal did not verify this transaction: Unknown error';
                     break;
                 case 200 != $response['response']['code']:
                     $reason = sprintf('PayPal did not verify the transaction: Code %s', $response['response']['code']);
                     break;
                 case empty($response['body']):
                     $reason = 'PayPal did not verify this transaction: Empty response';
                     break;
                 case 'VERIFIED' != $response['body']:
                     $reason = sprintf('PayPal did not verify this transaction: "%s"', $response['body']);
                     break;
                 case !$invoice->id:
                     $reason = sprintf('Specified invoice does not exist: "%s"', $invoice_id);
                     break;
             }
             $notes = 'Response Error: ' . $reason;
             $exit = true;
         }
     } else {
         // Did not find expected POST variables. Possible access attempt from a non PayPal site.
         $u_agent = $_SERVER['HTTP_USER_AGENT'];
         if (!$log && false === strpos($u_agent, 'PayPal')) {
             // Very likely someone tried to open the URL manually. Redirect to home page
             if (!$notes) {
                 $notes = 'Ignored: Missing POST variables. Redirect to Home-URL.';
             }
             $redirect = MS_Helper_Utility::home_url('/');
             $ignore = true;
             $success = false;
         } elseif ('m1' == $ext_type) {
             /*
              * The payment belongs to an imported M1 subscription and could
              * not be auto-matched.
              * Do not return an error code, but also do not modify any
              * invoice/subscription.
              */
             $notes = 'M1 Payment detected. Manual matching required.';
             $ignore = false;
             $success = false;
         } elseif ('pay_btn' == $ext_type) {
             /*
              * The payment was made by a PayPal Payment button that was
              * created in the PayPal account and not by M1/M2.
              */
             $notes = 'PayPal Payment button detected. Manual matching required.';
             $ignore = false;
             $success = false;
         } else {
             // PayPal sent us a IPN notice about a non-Membership payment:
             // Ignore it, but add it to the logs.
             if (!empty($notes)) {
                 // We already have an error message, do nothing.
             } elseif (!$payment_status || !$transaction_type) {
                 $notes = 'Ignored: Payment_status or txn_type not specified. Cannot process.';
             } elseif (empty($_POST['invoice']) && empty($_POST['custom'])) {
                 $notes = 'Ignored: No invoice or custom data specified.';
             } else {
                 $notes = 'Ignored: Missing POST variables. Identification is not possible.';
             }
             $ignore = true;
             $success = false;
         }
         $exit = true;
     }
     if ($ignore && !$success) {
         $success = null;
         $notes .= ' [Irrelevant IPN call]';
     }
     if (!$log) {
         do_action('ms_gateway_transaction_log', self::ID, 'handle', $success, $subscription_id, $invoice_id, $amount, $notes, $external_id);
         if ($redirect) {
             wp_safe_redirect($redirect);
             exit;
         }
         if ($exit) {
             exit;
         }
     } else {
         $log->invoice_id = $invoice_id;
         $log->subscription_id = $subscription_id;
         $log->amount = $amount;
         $log->description = $notes;
         $log->external_id = $external_id;
         if ($success) {
             $log->manual_state('ok');
         } elseif ($ignore) {
             $log->manual_state('ignore');
         }
         $log->save();
     }
     do_action('ms_gateway_paypalstandard_handle_return_after', $this);
     if ($log) {
         return $log;
     }
 }