/**
  * 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;
 }
/**
 * Decreases the sale count of a download. Primarily for when a purchase is
 * refunded.
 *
 * @since 1.0.8.1
 * @param int $download_id Download ID
 * @return bool|int
 */
function edd_decrease_purchase_count($download_id = 0, $quantity = 1)
{
    $download = new EDD_Download($download_id);
    return $download->decrease_sales($quantity);
}
 /**
  * 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;
 }