/**
 * Complete a purchase
 *
 * Performs all necessary actions to complete a purchase.
 * Triggered by the edd_update_payment_status() function.
 *
 * @since 1.0.8.3
 * @param int $payment_id the ID number of the payment
 * @param string $new_status the status of the payment, probably "publish"
 * @param string $old_status the status of the payment prior to being marked as "complete", probably "pending"
 * @return void
*/
function edd_complete_purchase($payment_id, $new_status, $old_status)
{
    if ($old_status == 'publish' || $old_status == 'complete') {
        return;
    }
    // Make sure that payments are only completed once
    // Make sure the payment completion is only processed when new status is complete
    if ($new_status != 'publish' && $new_status != 'complete') {
        return;
    }
    if (edd_is_test_mode() && !apply_filters('edd_log_test_payment_stats', false)) {
        return;
    }
    $payment_data = edd_get_payment_meta($payment_id);
    $downloads = maybe_unserialize($payment_data['downloads']);
    $user_info = maybe_unserialize($payment_data['user_info']);
    $cart_details = maybe_unserialize($payment_data['cart_details']);
    if (is_array($downloads)) {
        // Increase purchase count and earnings
        foreach ($downloads as $download) {
            edd_record_sale_in_log($download['id'], $payment_id, $user_info);
            edd_increase_purchase_count($download['id']);
            $amount = null;
            if (is_array($cart_details)) {
                foreach ($cart_details as $key => $item) {
                    if (array_search($download['id'], $item)) {
                        $cart_item_id = $key;
                    }
                }
                $amount = isset($cart_details[$cart_item_id]['price']) ? $cart_details[$cart_item_id]['price'] : null;
            }
            $amount = edd_get_download_final_price($download['id'], $user_info, $amount);
            edd_increase_earnings($download['id'], $amount);
        }
        // Clear the total earnings cache
        delete_transient('edd_earnings_total');
    }
    if (isset($user_info['discount']) && $user_info['discount'] != 'none') {
        edd_increase_discount_usage($user_info['discount']);
    }
    // Empty the shopping cart
    edd_empty_cart();
}
/**
 * Complete a purchase
 *
 * Performs all necessary actions to complete a purchase. 
 * Triggered by the edd_update_payment_status() function.
 *
 * @param		 int $payment_id the ID number of the payment
 * @param		 string $new_status the status of the payment, probably "publish"
 * @param		 string $old_status the status of the payment prior to being marked as "complete", probably "pending"
 * @access      private
 * @since       1.0.8.3
 * @return      void
*/
function edd_complete_purchase($payment_id, $new_status, $old_status)
{
    if ($old_status == 'publish' || $old_status == 'complete') {
        return;
    }
    // make sure that payments are only completed once
    if (!edd_is_test_mode()) {
        $payment_data = edd_get_payment_meta($payment_id);
        $downloads = maybe_unserialize($payment_data['downloads']);
        $user_info = maybe_unserialize($payment_data['user_info']);
        $cart_details = maybe_unserialize($payment_data['cart_details']);
        if (is_array($downloads)) {
            // increase purchase count and earnings
            foreach ($downloads as $download) {
                edd_record_sale_in_log($download['id'], $payment_id, $user_info, $payment_data['date']);
                edd_increase_purchase_count($download['id']);
                $amount = null;
                if (is_array($cart_details)) {
                    foreach ($cart_details as $key => $item) {
                        if (array_search($download['id'], $item)) {
                            $cart_item_id = $key;
                        }
                    }
                    $amount = isset($cart_details[$cart_item_id]['price']) ? $cart_details[$cart_item_id]['price'] : null;
                }
                $amount = edd_get_download_final_price($download['id'], $user_info, $amount);
                edd_increase_earnings($download['id'], $amount);
            }
        }
        if (isset($user_info['discount'])) {
            edd_increase_discount_usage($user_info['discount']);
        }
    }
    // empty the shopping cart
    edd_empty_cart();
}
/**
 * Complete a purchase
 *
 * Performs all necessary actions to complete a purchase.
 * Triggered by the edd_update_payment_status() function.
 *
 * @since 1.0.8.3
 * @param int $payment_id the ID number of the payment
 * @param string $new_status the status of the payment, probably "publish"
 * @param string $old_status the status of the payment prior to being marked as "complete", probably "pending"
 * @return void
*/
function edd_complete_purchase($payment_id, $new_status, $old_status)
{
    if ($old_status == 'publish' || $old_status == 'complete') {
        return;
    }
    // Make sure that payments are only completed once
    // Make sure the payment completion is only processed when new status is complete
    if ($new_status != 'publish' && $new_status != 'complete') {
        return;
    }
    $creation_date = get_post_field('post_date', $payment_id, 'raw');
    $completed_date = edd_get_payment_completed_date($payment_id);
    $user_info = edd_get_payment_meta_user_info($payment_id);
    $customer_id = edd_get_payment_customer_id($payment_id);
    $amount = edd_get_payment_amount($payment_id);
    $cart_details = edd_get_payment_meta_cart_details($payment_id);
    do_action('edd_pre_complete_purchase', $payment_id);
    if (is_array($cart_details)) {
        // Increase purchase count and earnings
        foreach ($cart_details as $cart_index => $download) {
            // "bundle" or "default"
            $download_type = edd_get_download_type($download['id']);
            $price_id = isset($download['item_number']['options']['price_id']) ? (int) $download['item_number']['options']['price_id'] : false;
            // Increase earnings and fire actions once per quantity number
            for ($i = 0; $i < $download['quantity']; $i++) {
                // Ensure these actions only run once, ever
                if (empty($completed_date)) {
                    edd_record_sale_in_log($download['id'], $payment_id, $price_id, $creation_date);
                    do_action('edd_complete_download_purchase', $download['id'], $payment_id, $download_type, $download, $cart_index);
                }
            }
            // Increase the earnings for this download ID
            edd_increase_earnings($download['id'], $download['price']);
            edd_increase_purchase_count($download['id'], $download['quantity']);
        }
        // Clear the total earnings cache
        delete_transient('edd_earnings_total');
        // Clear the This Month earnings (this_monththis_month is NOT a typo)
        delete_transient(md5('edd_earnings_this_monththis_month'));
        delete_transient(md5('edd_earnings_todaytoday'));
    }
    // Increase the customer's purchase stats
    $customer = new EDD_Customer($customer_id);
    $customer->increase_purchase_count();
    $customer->increase_value($amount);
    edd_increase_total_earnings($amount);
    // Check for discount codes and increment their use counts
    if (!empty($user_info['discount']) && $user_info['discount'] !== 'none') {
        $discounts = array_map('trim', explode(',', $user_info['discount']));
        if (!empty($discounts)) {
            foreach ($discounts as $code) {
                edd_increase_discount_usage($code);
            }
        }
    }
    // Ensure this action only runs once ever
    if (empty($completed_date)) {
        // Save the completed date
        edd_update_payment_meta($payment_id, '_edd_completed_date', current_time('mysql'));
        do_action('edd_complete_purchase', $payment_id);
    }
    // Empty the shopping cart
    edd_empty_cart();
}
 /**
  * One items have been set, an update is needed to save them to the database.
  *
  * @return bool  True of the save occured, false if it failed or wasn't needed
  */
 public function save()
 {
     $saved = false;
     if (empty($this->ID)) {
         $payment_id = $this->insert_payment();
         if (false === $payment_id) {
             $saved = false;
         } else {
             $this->ID = $payment_id;
         }
     }
     if ($this->ID !== $this->_ID) {
         $this->ID = $this->_ID;
     }
     // If we have something pending, let's save it
     if (!empty($this->pending)) {
         $total_increase = 0;
         $total_decrease = 0;
         foreach ($this->pending as $key => $value) {
             switch ($key) {
                 case 'downloads':
                     // Update totals for pending downloads
                     foreach ($this->pending[$key] as $item) {
                         switch ($item['action']) {
                             case 'add':
                                 $price = $item['price'];
                                 $taxes = $item['tax'];
                                 if ('publish' === $this->status || 'complete' === $this->status || 'revoked' === $this->status) {
                                     // Add sales logs
                                     $log_date = date('Y-m-d G:i:s', current_time('timestamp', true));
                                     $price_id = isset($item['item_number']['options']['price_id']) ? $item['item_number']['options']['price_id'] : 0;
                                     $y = 0;
                                     while ($y < $item['quantity']) {
                                         edd_record_sale_in_log($item['id'], $this->ID, $price_id, $log_date);
                                         $y++;
                                     }
                                     $download = new EDD_Download($item['id']);
                                     $download->increase_sales($item['quantity']);
                                     $download->increase_earnings($price);
                                     $total_increase += $price;
                                 }
                                 break;
                             case 'remove':
                                 $log_args = array('post_type' => 'edd_log', 'post_parent' => $item['id'], 'numberposts' => $item['quantity'], 'meta_query' => array(array('key' => '_edd_log_payment_id', 'value' => $this->ID, 'compare' => '='), array('key' => '_edd_log_price_id', 'value' => $item['price_id'], 'compare' => '=')));
                                 $found_logs = get_posts($log_args);
                                 foreach ($found_logs as $log) {
                                     wp_delete_post($log->ID, true);
                                 }
                                 if ('publish' === $this->status || 'complete' === $this->status || 'revoked' === $this->status) {
                                     $download = new EDD_Download($item['id']);
                                     $download->decrease_sales($item['quantity']);
                                     $download->decrease_earnings($item['amount']);
                                     $total_decrease += $item['amount'];
                                 }
                                 break;
                         }
                     }
                     break;
                 case 'fees':
                     if (!empty($this->pending[$key])) {
                         foreach ($this->pending[$key] as $fee) {
                             switch ($fee['action']) {
                                 case 'add':
                                     $total_increase += $fee['amount'];
                                     break;
                                 case 'remove':
                                     $total_decrease += $fee['amount'];
                                     break;
                             }
                         }
                     }
                     break;
                 case 'status':
                     $this->update_status($this->status);
                     break;
                 case 'gateway':
                     $this->update_meta('_edd_payment_gateway', $this->gateway);
                     break;
                 case 'mode':
                     $this->update_meta('_edd_payment_mode', $this->mode);
                     break;
                 case 'transaction_id':
                     $this->update_meta('_edd_payment_transaction_id', $this->transaction_id);
                     break;
                 case 'ip':
                     $this->update_meta('_edd_payment_user_ip', $this->ip);
                     break;
                 case 'customer_id':
                     $this->update_meta('_edd_payment_customer_id', $this->customer_id);
                     break;
                 case 'user_id':
                     $this->update_meta('_edd_payment_user_id', $this->user_id);
                     break;
                 case 'first_name':
                     $this->user_info['first_name'] = $this->first_name;
                     break;
                 case 'last_name':
                     $this->user_info['last_name'] = $this->last_name;
                     break;
                 case 'discounts':
                     $this->user_info['discount'] = $this->discounts;
                     break;
                 case 'address':
                     $this->user_info['address'] = $this->address;
                     break;
                 case 'email':
                     $this->update_meta('_edd_payment_user_email', $this->email);
                     break;
                 case 'key':
                     $this->update_meta('_edd_payment_purchase_key', $this->key);
                     break;
                 case 'number':
                     $this->update_meta('_edd_payment_number', $this->number);
                     break;
                 case 'completed_date':
                     $this->update_meta('_edd_completed_date', $this->completed_date);
                     break;
                 case 'has_unlimited_downloads':
                     $this->update_meta('_edd_payment_unlimited_downloads', $this->has_unlimited_downloads);
                     break;
                 case 'parent_payment':
                     $args = array('ID' => $this->ID, 'post_parent' => $this->parent_payment);
                     wp_update_post($args);
                     break;
                 default:
                     do_action('edd_payment_save', $this, $key);
                     break;
             }
         }
         if ('pending' !== $this->status) {
             $customer = new EDD_Customer($this->customer_id);
             $total_change = $total_increase - $total_decrease;
             if ($total_change < 0) {
                 $total_chnage = -$total_change;
                 // Decrease the customer's purchase stats
                 $customer->decrease_value($total_change);
                 edd_decrease_total_earnings($total_change);
             } else {
                 if ($total_change > 0) {
                     // Increase the customer's purchase stats
                     $customer->increase_value($total_change);
                     edd_increase_total_earnings($total_change);
                 }
             }
         }
         $this->update_meta('_edd_payment_total', $this->total);
         $this->update_meta('_edd_payment_tax', $this->tax);
         $new_meta = array('downloads' => $this->downloads, 'cart_details' => $this->cart_details, 'fees' => $this->fees, 'currency' => $this->currency, 'user_info' => $this->user_info);
         $meta = $this->get_meta();
         $merged_meta = array_merge($meta, $new_meta);
         // Only save the payment meta if it's changed
         if (md5(serialize($meta)) !== md5(serialize($merged_meta))) {
             $updated = $this->update_meta('_edd_payment_meta', $merged_meta);
             if (false !== $updated) {
                 $saved = true;
             }
         }
         $this->pending = array();
         $saved = true;
     }
     if (true === $saved) {
         $this->setup_payment($this->ID);
     }
     return $saved;
 }
/**
 * Process the payment details edit
 *
 * @access      private
 * @since       1.9
 * @return      void
*/
function edd_update_payment_details($data)
{
    if (!current_user_can('edit_shop_payments', $data['edd_payment_id'])) {
        wp_die(__('You do not have permission to edit this payment record', 'easy-digital-downloads'), __('Error', 'easy-digital-downloads'), array('response' => 403));
    }
    check_admin_referer('edd_update_payment_details_nonce');
    // Retrieve the payment ID
    $payment_id = absint($data['edd_payment_id']);
    // Retrieve existing payment meta
    $meta = edd_get_payment_meta($payment_id);
    $user_info = edd_get_payment_meta_user_info($payment_id);
    $status = $data['edd-payment-status'];
    $unlimited = isset($data['edd-unlimited-downloads']) ? '1' : '';
    $date = sanitize_text_field($data['edd-payment-date']);
    $hour = sanitize_text_field($data['edd-payment-time-hour']);
    // Restrict to our high and low
    if ($hour > 23) {
        $hour = 23;
    } elseif ($hour < 0) {
        $hour = 00;
    }
    $minute = sanitize_text_field($data['edd-payment-time-min']);
    // Restrict to our high and low
    if ($minute > 59) {
        $minute = 59;
    } elseif ($minute < 0) {
        $minute = 00;
    }
    $address = array_map('trim', $data['edd-payment-address'][0]);
    $curr_total = edd_sanitize_amount(edd_get_payment_amount($payment_id));
    $new_total = edd_sanitize_amount($_POST['edd-payment-total']);
    $tax = isset($_POST['edd-payment-tax']) ? edd_sanitize_amount($_POST['edd-payment-tax']) : 0;
    $date = date('Y-m-d', strtotime($date)) . ' ' . $hour . ':' . $minute . ':00';
    $curr_customer_id = sanitize_text_field($data['edd-current-customer']);
    $new_customer_id = sanitize_text_field($data['customer-id']);
    // Setup purchased Downloads and price options
    $updated_downloads = isset($_POST['edd-payment-details-downloads']) ? $_POST['edd-payment-details-downloads'] : false;
    if ($updated_downloads && !empty($_POST['edd-payment-downloads-changed'])) {
        $downloads = array();
        $cart_details = array();
        $i = 0;
        foreach ($updated_downloads as $download) {
            if (empty($download['amount'])) {
                $download['amount'] = '0.00';
            }
            $item = array();
            $item['id'] = absint($download['id']);
            $item['quantity'] = absint($download['quantity']) > 0 ? absint($download['quantity']) : 1;
            $price_id = (int) $download['price_id'];
            $has_log = absint($download['has_log']);
            if ($price_id !== false && edd_has_variable_prices($item['id'])) {
                $item['options'] = array('price_id' => $price_id);
            }
            $downloads[] = $item;
            $cart_item = array();
            $cart_item['item_number'] = $item;
            $item_price = round($download['amount'] / $item['quantity'], edd_currency_decimal_filter());
            $cart_details[$i] = array('name' => get_the_title($download['id']), 'id' => $download['id'], 'item_number' => $item, 'price' => $download['amount'], 'item_price' => $item_price, 'subtotal' => $download['amount'], 'quantity' => $download['quantity'], 'discount' => 0, 'tax' => 0);
            // If this item doesn't have a log yet, add one for each quantity count
            if (empty($has_log)) {
                $log_date = date('Y-m-d G:i:s', current_time('timestamp', true));
                $price_id = $price_id !== false ? $price_id : 0;
                $y = 0;
                while ($y < $download['quantity']) {
                    edd_record_sale_in_log($download['id'], $payment_id, $price_id, $log_date);
                    $y++;
                }
                edd_increase_purchase_count($download['id'], $download['quantity']);
                edd_increase_earnings($download['id'], $download['amount']);
            }
            $i++;
        }
        $meta['downloads'] = $downloads;
        $meta['cart_details'] = $cart_details;
        $deleted_downloads = json_decode(stripcslashes($data['edd-payment-removed']), true);
        foreach ($deleted_downloads as $deleted_download) {
            $deleted_download = $deleted_download[0];
            if (empty($deleted_download['id'])) {
                continue;
            }
            $price_id = empty($deleted_download['price_id']) ? 0 : (int) $deleted_download['price_id'];
            $log_args = array('post_type' => 'edd_log', 'post_parent' => $deleted_download['id'], 'numberposts' => $deleted_download['quantity'], 'meta_query' => array(array('key' => '_edd_log_payment_id', 'value' => $payment_id, 'compare' => '='), array('key' => '_edd_log_price_id', 'value' => $price_id, 'compare' => '=')));
            $found_logs = get_posts($log_args);
            foreach ($found_logs as $log) {
                wp_delete_post($log->ID, true);
            }
            edd_decrease_purchase_count($deleted_download['id'], $deleted_download['quantity']);
            edd_decrease_earnings($deleted_download['id'], $deleted_download['amount']);
            do_action('edd_remove_download_from_payment', $payment_id, $deleted_download['id']);
        }
    }
    do_action('edd_update_edited_purchase', $payment_id);
    // Update main payment record
    $updated = wp_update_post(array('ID' => $payment_id, 'post_date' => $date));
    if (0 === $updated) {
        wp_die(__('Error Updating Payment', 'easy-digital-downloads'), __('Error', 'easy-digital-downloads'), array('response' => 400));
    }
    $customer_changed = false;
    if (isset($data['edd-new-customer']) && $data['edd-new-customer'] == '1') {
        $email = isset($data['edd-new-customer-email']) ? sanitize_text_field($data['edd-new-customer-email']) : '';
        $names = isset($data['edd-new-customer-name']) ? sanitize_text_field($data['edd-new-customer-name']) : '';
        if (empty($email) || empty($names)) {
            wp_die(__('New Customers require a name and email address', 'easy-digital-downloads'));
        }
        $customer = new EDD_Customer($email);
        if (empty($customer->id)) {
            $customer_data = array('name' => $names, 'email' => $email);
            $user_id = email_exists($email);
            if (false !== $user_id) {
                $customer_data['user_id'] = $user_id;
            }
            if (!$customer->create($customer_data)) {
                // Failed to crete the new customer, assume the previous customer
                $customer_changed = false;
                $customer = new EDD_Customer($curr_customer_id);
                edd_set_error('edd-payment-new-customer-fail', __('Error creating new customer', 'easy-digital-downloads'));
            }
        }
        $new_customer_id = $customer->id;
        $previous_customer = new EDD_Customer($curr_customer_id);
        $customer_changed = true;
    } elseif ($curr_customer_id !== $new_customer_id) {
        $customer = new EDD_Customer($new_customer_id);
        $email = $customer->email;
        $names = $customer->name;
        $previous_customer = new EDD_Customer($curr_customer_id);
        $customer_changed = true;
    } else {
        $customer = new EDD_Customer($curr_customer_id);
        $email = $customer->email;
        $names = $customer->name;
    }
    // Setup first and last name from input values
    $names = explode(' ', $names);
    $first_name = !empty($names[0]) ? $names[0] : '';
    $last_name = '';
    if (!empty($names[1])) {
        unset($names[0]);
        $last_name = implode(' ', $names);
    }
    if ($customer_changed) {
        // Remove the stats and payment from the previous customer and attach it to the new customer
        $previous_customer->remove_payment($payment_id, false);
        $customer->attach_payment($payment_id, false);
        // If purchase was completed and not ever refunded, adjust stats of customers
        if ('revoked' == $status || 'publish' == $status) {
            $previous_customer->decrease_purchase_count();
            $previous_customer->decrease_value($new_total);
            $customer->increase_purchase_count();
            $customer->increase_value($new_total);
        }
        update_post_meta($payment_id, '_edd_payment_customer_id', $customer->id);
    }
    // Set new meta values
    $user_info['id'] = $customer->user_id;
    $user_info['email'] = $customer->email;
    $user_info['first_name'] = $first_name;
    $user_info['last_name'] = $last_name;
    $user_info['address'] = $address;
    $meta['user_info'] = $user_info;
    $meta['tax'] = $tax;
    // Check for payment notes
    if (!empty($data['edd-payment-note'])) {
        $note = wp_kses($data['edd-payment-note'], array());
        edd_insert_payment_note($payment_id, $note);
    }
    // Set new status
    edd_update_payment_status($payment_id, $status);
    edd_update_payment_meta($payment_id, '_edd_payment_user_id', $customer->user_id);
    edd_update_payment_meta($payment_id, '_edd_payment_user_email', $customer->email);
    edd_update_payment_meta($payment_id, '_edd_payment_meta', $meta);
    edd_update_payment_meta($payment_id, '_edd_payment_total', $new_total);
    // Adjust total store earnings if the payment total has been changed
    if ($new_total !== $curr_total && ('publish' == $status || 'revoked' == $status)) {
        if ($new_total > $curr_total) {
            // Increase if our new total is higher
            $difference = $new_total - $curr_total;
            edd_increase_total_earnings($difference);
        } elseif ($curr_total > $new_total) {
            // Decrease if our new total is lower
            $difference = $curr_total - $new_total;
            edd_decrease_total_earnings($difference);
        }
    }
    edd_update_payment_meta($payment_id, '_edd_payment_downloads', $new_total);
    edd_update_payment_meta($payment_id, '_edd_payment_unlimited_downloads', $unlimited);
    do_action('edd_updated_edited_purchase', $payment_id);
    wp_safe_redirect(admin_url('edit.php?post_type=download&page=edd-payment-history&view=view-order-details&edd-message=payment-updated&id=' . $payment_id));
    exit;
}
 /**
  * One items have been set, an update is needed to save them to the database.
  *
  * @return bool  True of the save occurred, false if it failed or wasn't needed
  */
 public function save()
 {
     $saved = false;
     if (empty($this->ID)) {
         $payment_id = $this->insert_payment();
         if (false === $payment_id) {
             $saved = false;
         } else {
             $this->ID = $payment_id;
         }
     }
     if ($this->ID !== $this->_ID) {
         $this->ID = $this->_ID;
     }
     // If we have something pending, let's save it
     if (!empty($this->pending)) {
         $total_increase = 0;
         $total_decrease = 0;
         foreach ($this->pending as $key => $value) {
             switch ($key) {
                 case 'downloads':
                     // Update totals for pending downloads
                     foreach ($this->pending[$key] as $item) {
                         switch ($item['action']) {
                             case 'add':
                                 $price = $item['price'];
                                 $taxes = $item['tax'];
                                 if ('publish' === $this->status || 'complete' === $this->status || 'revoked' === $this->status) {
                                     // Add sales logs
                                     $log_date = date_i18n('Y-m-d G:i:s', current_time('timestamp'));
                                     $price_id = isset($item['item_number']['options']['price_id']) ? $item['item_number']['options']['price_id'] : 0;
                                     $y = 0;
                                     while ($y < $item['quantity']) {
                                         edd_record_sale_in_log($item['id'], $this->ID, $price_id, $log_date);
                                         $y++;
                                     }
                                     $increase_earnings = $price;
                                     if (!empty($item['fees'])) {
                                         foreach ($item['fees'] as $fee) {
                                             // Only let negative fees affect the earnings
                                             if ($fee['amount'] > 0) {
                                                 continue;
                                             }
                                             $increase_earnings += (double) $fee['amount'];
                                         }
                                     }
                                     $download = new EDD_Download($item['id']);
                                     $download->increase_sales($item['quantity']);
                                     $download->increase_earnings($increase_earnings);
                                     $total_increase += $price;
                                 }
                                 break;
                             case 'remove':
                                 $log_args = array('post_type' => 'edd_log', 'post_parent' => $item['id'], 'numberposts' => $item['quantity'], 'meta_query' => array(array('key' => '_edd_log_payment_id', 'value' => $this->ID, 'compare' => '='), array('key' => '_edd_log_price_id', 'value' => $item['price_id'], 'compare' => '=')));
                                 $found_logs = get_posts($log_args);
                                 foreach ($found_logs as $log) {
                                     wp_delete_post($log->ID, true);
                                 }
                                 if ('publish' === $this->status || 'complete' === $this->status || 'revoked' === $this->status) {
                                     $download = new EDD_Download($item['id']);
                                     $download->decrease_sales($item['quantity']);
                                     $decrease_amount = $item['amount'];
                                     if (!empty($item['fees'])) {
                                         foreach ($item['fees'] as $fee) {
                                             // Only let negative fees affect the earnings
                                             if ($fee['amount'] > 0) {
                                                 continue;
                                             }
                                             $decrease_amount += $fee['amount'];
                                         }
                                     }
                                     $download->decrease_earnings($decrease_amount);
                                     $total_decrease += $item['amount'];
                                 }
                                 break;
                         }
                     }
                     break;
                 case 'fees':
                     if ('publish' !== $this->status && 'complete' !== $this->status && 'revoked' !== $this->status) {
                         break;
                     }
                     if (empty($this->pending[$key])) {
                         break;
                     }
                     foreach ($this->pending[$key] as $fee) {
                         switch ($fee['action']) {
                             case 'add':
                                 $total_increase += $fee['amount'];
                                 break;
                             case 'remove':
                                 $total_decrease += $fee['amount'];
                                 break;
                         }
                     }
                     break;
                 case 'status':
                     $this->update_status($this->status);
                     break;
                 case 'gateway':
                     $this->update_meta('_edd_payment_gateway', $this->gateway);
                     break;
                 case 'mode':
                     $this->update_meta('_edd_payment_mode', $this->mode);
                     break;
                 case 'transaction_id':
                     $this->update_meta('_edd_payment_transaction_id', $this->transaction_id);
                     break;
                 case 'ip':
                     $this->update_meta('_edd_payment_user_ip', $this->ip);
                     break;
                 case 'customer_id':
                     $this->update_meta('_edd_payment_customer_id', $this->customer_id);
                     break;
                 case 'user_id':
                     $this->update_meta('_edd_payment_user_id', $this->user_id);
                     break;
                 case 'first_name':
                     $this->user_info['first_name'] = $this->first_name;
                     break;
                 case 'last_name':
                     $this->user_info['last_name'] = $this->last_name;
                     break;
                 case 'discounts':
                     if (!is_array($this->discounts)) {
                         $this->discounts = explode(',', $this->discounts);
                     }
                     $this->user_info['discount'] = implode(',', $this->discounts);
                     break;
                 case 'address':
                     $this->user_info['address'] = $this->address;
                     break;
                 case 'email':
                     $this->update_meta('_edd_payment_user_email', $this->email);
                     break;
                 case 'key':
                     $this->update_meta('_edd_payment_purchase_key', $this->key);
                     break;
                 case 'number':
                     $this->update_meta('_edd_payment_number', $this->number);
                     break;
                 case 'date':
                     $args = array('ID' => $this->ID, 'post_date' => $this->date, 'edit_date' => true);
                     wp_update_post($args);
                     break;
                 case 'completed_date':
                     $this->update_meta('_edd_completed_date', $this->completed_date);
                     break;
                 case 'has_unlimited_downloads':
                     $this->update_meta('_edd_payment_unlimited_downloads', $this->has_unlimited_downloads);
                     break;
                 case 'parent_payment':
                     $args = array('ID' => $this->ID, 'post_parent' => $this->parent_payment);
                     wp_update_post($args);
                     break;
                 default:
                     /**
                      * Used to save non-standard data. Developers can hook here if they want to save
                      * specific payment data when $payment->save() is run and their item is in the $pending array
                      */
                     do_action('edd_payment_save', $this, $key);
                     break;
             }
         }
         if ('pending' !== $this->status) {
             $customer = new EDD_Customer($this->customer_id);
             $total_change = $total_increase - $total_decrease;
             if ($total_change < 0) {
                 $total_change = -$total_change;
                 // Decrease the customer's purchase stats
                 $customer->decrease_value($total_change);
                 edd_decrease_total_earnings($total_change);
             } else {
                 if ($total_change > 0) {
                     // Increase the customer's purchase stats
                     $customer->increase_value($total_change);
                     edd_increase_total_earnings($total_change);
                 }
             }
         }
         $this->update_meta('_edd_payment_total', $this->total);
         $this->update_meta('_edd_payment_tax', $this->tax);
         $this->downloads = array_values($this->downloads);
         $new_meta = array('downloads' => $this->downloads, 'cart_details' => $this->cart_details, 'fees' => $this->fees, 'currency' => $this->currency, 'user_info' => is_array($this->user_info) ? array_filter($this->user_info) : array(), 'date' => $this->date);
         // Do some merging of user_info before we merge it all, to honor the edd_payment_meta filter
         if (!empty($this->payment_meta['user_info'])) {
             $stored_discount = !empty($new_meta['user_info']['discount']) ? $new_meta['user_info']['discount'] : '';
             $new_meta['user_info'] = array_replace_recursive((array) $this->payment_meta['user_info'], $new_meta['user_info']);
             if ('none' !== $stored_discount) {
                 $new_meta['user_info']['discount'] = $stored_discount;
             }
         }
         $meta = $this->get_meta();
         $merged_meta = array_merge($meta, $new_meta);
         // Only save the payment meta if it's changed
         if (md5(serialize($meta)) !== md5(serialize($merged_meta))) {
             $updated = $this->update_meta('_edd_payment_meta', $merged_meta);
             if (false !== $updated) {
                 $saved = true;
             }
         }
         $this->pending = array();
         $saved = true;
     }
     if (true === $saved) {
         $this->setup_payment($this->ID);
         /**
          * This action fires anytime that $payment->save() is run, allowing developers to run actions
          * when a payment is updated
          */
         do_action('edd_payment_saved', $this->ID, $this);
     }
     return $saved;
 }