/**
  * Prepare html fields.
  *
  * @since  1.0.0
  *
  * @return array
  */
 function prepare_fields()
 {
     $invoice = $this->data['invoice'];
     $currency = MS_Plugin::instance()->settings->currency;
     $user_name = '';
     $transaction_link = '';
     $user_id = 0;
     $user_list = array();
     if ($invoice->id) {
         $member = $invoice->get_member();
         $user_id = $member->id;
         $user_name = $member->name;
         $transaction_link = sprintf('<a href="%s" target="_blank">%s</a>', MS_Controller_Plugin::get_admin_url('billing', array('show' => 'logs', 'invoice' => $invoice->id)), __('Show Transactions', 'membership2'));
     } else {
         $user_list = MS_Model_Member::get_usernames(null, MS_Model_Member::SEARCH_ALL_USERS);
     }
     $fields = array('link_transactions' => array('id' => 'link_transactions', 'title' => $transaction_link, 'type' => MS_Helper_Html::TYPE_HTML_TEXT, 'wrapper_class' => 'ms-transactions-link'), 'txt_user' => array('id' => 'txt_user', 'title' => __('Invoice for member', 'membership2'), 'type' => MS_Helper_Html::TYPE_HTML_TEXT, 'value' => sprintf('<a href="%s">%s</a>', MS_Controller_Plugin::get_admin_url('add-member', array('user_id' => $user_id)), $user_name)), 'txt_membership' => array('id' => 'txt_membership', 'title' => __('Payment for membership', 'membership2'), 'type' => MS_Helper_Html::TYPE_HTML_TEXT), 'txt_created' => array('id' => 'txt_created', 'title' => __('Invoice created on', 'membership2'), 'type' => MS_Helper_Html::TYPE_HTML_TEXT), 'txt_separator' => array('type' => MS_Helper_Html::TYPE_HTML_SEPARATOR), 'status' => array('id' => 'status', 'title' => __('Invoice status', 'membership2'), 'type' => MS_Helper_Html::INPUT_TYPE_SELECT, 'field_options' => MS_Model_Invoice::get_status_types(true), 'value' => $invoice->status), 'user_id' => array('id' => 'user_id', 'title' => __('Invoice for member', 'membership2'), 'type' => MS_Helper_Html::INPUT_TYPE_SELECT, 'value' => $invoice->user_id, 'field_options' => $user_list), 'membership_id' => array('id' => 'membership_id', 'title' => __('Payment for membership', 'membership2'), 'type' => MS_Helper_Html::INPUT_TYPE_SELECT, 'value' => $invoice->membership_id, 'field_options' => $this->data['memberships']), 'amount' => array('id' => 'amount', 'title' => sprintf(__('Amount (%s)', 'membership2'), $currency), 'type' => MS_Helper_Html::INPUT_TYPE_NUMBER, 'value' => MS_Helper_Billing::format_price($invoice->amount), 'config' => array('step' => 'any', 'min' => 0)), 'discount' => array('id' => 'discount', 'title' => sprintf(__('Discount (%s)', 'membership2'), $currency), 'type' => MS_Helper_Html::INPUT_TYPE_NUMBER, 'value' => MS_Helper_Billing::format_price($invoice->discount), 'config' => array('step' => 'any', 'min' => 0)), 'due_date' => array('id' => 'due_date', 'title' => __('Due date', 'membership2'), 'type' => MS_Helper_Html::INPUT_TYPE_DATEPICKER, 'value' => $invoice->due_date), 'description' => array('id' => 'description', 'title' => __('Description', 'membership2'), 'type' => MS_Helper_Html::INPUT_TYPE_TEXT, 'class' => 'widefat', 'value' => $invoice->description), 'notes' => array('id' => 'notes', 'title' => __('Notes', 'membership2'), 'type' => MS_Helper_Html::INPUT_TYPE_TEXT_AREA, 'class' => 'widefat', 'value' => $invoice->get_notes_desc()), 'invoice_id' => array('id' => 'invoice_id', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $invoice->id), '_wpnonce' => array('id' => '_wpnonce', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => wp_create_nonce($this->data['action'])), 'action' => array('id' => 'action', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $this->data['action']), 'separator' => array('type' => MS_Helper_Html::TYPE_HTML_SEPARATOR), 'execute' => array('id' => 'execute', 'title' => __('Execute status change actions on Save (add/remove membership)', 'membership2'), 'type' => MS_Helper_Html::INPUT_TYPE_CHECKBOX, 'value' => true), 'cancel' => array('id' => 'cancel', 'type' => MS_Helper_Html::TYPE_HTML_LINK, 'title' => __('Cancel', 'membership2'), 'value' => __('Cancel', 'membership2'), 'url' => esc_url_raw(remove_query_arg(array('action', 'invoice_id'))), 'class' => 'wpmui-field-button button'), 'submit' => array('id' => 'submit', 'type' => MS_Helper_Html::INPUT_TYPE_SUBMIT, 'value' => __('Save Changes', 'membership2')));
     if ($invoice->id > 0) {
         $fields['user_id']['type'] = MS_Helper_Html::INPUT_TYPE_HIDDEN;
         $fields['membership_id']['type'] = MS_Helper_Html::INPUT_TYPE_HIDDEN;
         $fields['txt_membership']['value'] = $this->data['memberships'][$invoice->membership_id];
         $fields['txt_created']['value'] = MS_Helper_Period::format_date($invoice->invoice_date);
     } else {
         unset($fields['txt_user']);
         unset($fields['txt_membership']);
         unset($fields['txt_created']);
         unset($fields['txt_separator']);
     }
     return apply_filters('ms_view_billing_edit_prepare_fields', $fields, $this);
 }
 /**
  * Prepare html fields.
  *
  * @since  1.0.0
  *
  * @return array
  */
 function prepare_fields()
 {
     $invoice = $this->data['invoice'];
     $currency = MS_Plugin::instance()->settings->currency;
     $fields = array('txt_user' => array('id' => 'txt_user', 'title' => __('Username', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::TYPE_HTML_TEXT, 'value' => $this->data['users'][$invoice->user_id]), 'txt_membership' => array('id' => 'txt_membership', 'title' => __('Membership', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::TYPE_HTML_TEXT), 'txt_separator' => array('type' => MS_Helper_Html::TYPE_HTML_SEPARATOR), 'status' => array('id' => 'status', 'title' => __('Status', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::INPUT_TYPE_SELECT, 'field_options' => MS_Model_Invoice::get_status_types(), 'value' => $invoice->status), 'user_id' => array('id' => 'user_id', 'title' => __('Username', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::INPUT_TYPE_SELECT, 'value' => $invoice->user_id, 'field_options' => $this->data['users']), 'membership_id' => array('id' => 'membership_id', 'title' => __('Membership', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::INPUT_TYPE_SELECT, 'value' => $invoice->membership_id, 'field_options' => $this->data['memberships']), 'description' => array('id' => 'description', 'title' => __('Description', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::INPUT_TYPE_TEXT, 'value' => $invoice->description), 'amount' => array('id' => 'amount', 'title' => sprintf(__('Amount (%s)', MS_TEXT_DOMAIN), $currency), 'type' => MS_Helper_Html::INPUT_TYPE_TEXT, 'value' => $invoice->amount), 'discount' => array('id' => 'discount', 'title' => sprintf(__('Discount (%s)', MS_TEXT_DOMAIN), $currency), 'type' => MS_Helper_Html::INPUT_TYPE_TEXT, 'value' => $invoice->discount), 'due_date' => array('id' => 'due_date', 'title' => __('Due date', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::INPUT_TYPE_DATEPICKER, 'value' => $invoice->due_date), 'notes' => array('id' => 'notes', 'title' => __('Notes', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::INPUT_TYPE_TEXT_AREA, 'value' => $invoice->get_notes_desc()), 'invoice_id' => array('id' => 'invoice_id', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $invoice->id), '_wpnonce' => array('id' => '_wpnonce', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => wp_create_nonce($this->data['action'])), 'action' => array('id' => 'action', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $this->data['action']), 'separator' => array('type' => MS_Helper_Html::TYPE_HTML_SEPARATOR), 'execute' => array('id' => 'execute', 'title' => __('Execute status change actions on Save (add/remove membership)', MS_TEXT_DOMAIN), 'type' => MS_Helper_Html::INPUT_TYPE_CHECKBOX, 'value' => true), 'cancel' => array('id' => 'cancel', 'type' => MS_Helper_Html::TYPE_HTML_LINK, 'title' => __('Cancel', MS_TEXT_DOMAIN), 'value' => __('Cancel', MS_TEXT_DOMAIN), 'url' => esc_url_raw(remove_query_arg(array('action', 'invoice_id'))), 'class' => 'wpmui-field-button button'), 'submit' => array('id' => 'submit', 'type' => MS_Helper_Html::INPUT_TYPE_SUBMIT, 'value' => __('Save Changes', MS_TEXT_DOMAIN)));
     if ($invoice->id > 0) {
         $fields['user_id']['type'] = MS_Helper_Html::INPUT_TYPE_HIDDEN;
         $fields['membership_id']['type'] = MS_Helper_Html::INPUT_TYPE_HIDDEN;
         $fields['txt_membership']['value'] = $this->data['memberships'][$invoice->membership_id];
     } else {
         unset($fields['txt_user']);
         unset($fields['txt_membership']);
         unset($fields['txt_separator']);
     }
     return apply_filters('ms_view_billing_edit_prepare_fields', $fields, $this);
 }
 /**
  * Adds the Pro-Rating discount to an invoice.
  *
  * @since  1.0.1.0
  * @param  MS_Model_Invoice $invoice
  * @return MS_Model_Invoice Modified Invoice.
  */
 public function add_discount($invoice)
 {
     // Only the first invoice can be pro-rated.
     if ($invoice->invoice_number > 1) {
         return $invoice;
     }
     $subscription = $invoice->get_subscription();
     $membership = $invoice->get_membership();
     if (!$subscription->move_from_id) {
         return $invoice;
     }
     $ids = explode(',', $subscription->move_from_id);
     if (empty($ids)) {
         return $invoice;
     }
     if ($membership->is_free()) {
         return $invoice;
     }
     // Calc pro rate discount if moving from another membership.
     $pro_rate = 0;
     foreach ($ids as $id) {
         if (!$id) {
             continue;
         }
         $move_from = MS_Model_Relationship::get_subscription($subscription->user_id, $id);
         if ($move_from->is_valid() && $move_from->id == $id) {
             $pro_rate += $this->get_discount($move_from);
         }
     }
     $pro_rate = floatval(apply_filters('ms_addon_prorate_apply_discount', abs($pro_rate), $invoice));
     if ($pro_rate > $invoice->amount) {
         $pro_rate = $invoice->amount;
     }
     if ($pro_rate > 0) {
         $invoice->pro_rate = $pro_rate;
         $notes[] = sprintf(__('Pro-Rate Discount: %s.', MS_TEXT_DOMAIN) . ' ', $invoice->currency . ' ' . $pro_rate);
     }
     return $invoice;
 }
 /**
  * Adds the Pro-Rating discount to an invoice.
  *
  * @since  1.0.1.0
  * @param  MS_Model_Invoice $invoice
  * @return MS_Model_Invoice Modified Invoice.
  */
 public function add_discount($invoice)
 {
     $subscription = $invoice->get_subscription();
     // If memberships were already cancelled don't pro-rate again!
     if ($subscription->cancelled_memberships) {
         return $invoice;
     }
     $membership = $invoice->get_membership();
     if (!$subscription->move_from_id) {
         return $invoice;
     }
     $ids = explode(',', $subscription->move_from_id);
     if (empty($ids)) {
         return $invoice;
     }
     if ($membership->is_free()) {
         return $invoice;
     }
     // Calc pro rate discount if moving from another membership.
     $pro_rate = 0;
     foreach ($ids as $id) {
         if (!$id) {
             continue;
         }
         $move_from = MS_Model_Relationship::get_subscription($subscription->user_id, $id);
         if ($move_from->is_valid() && $move_from->membership_id == $id) {
             $pro_rate += $this->get_discount($move_from);
         }
     }
     $pro_rate = floatval(apply_filters('ms_addon_prorate_apply_discount', abs($pro_rate), $invoice));
     if ($pro_rate > $invoice->amount) {
         $pro_rate = $invoice->amount;
     }
     if ($pro_rate > 0) {
         $invoice->pro_rate = $pro_rate;
         $notes[] = sprintf(__('Pro-Rate Discount: %s.', 'membership2') . ' ', $invoice->currency . ' ' . $pro_rate);
     }
     return $invoice;
 }
 /**
  * Get post types that are part of this plugin.
  *
  * @since  1.0.0
  *
  * @return array The plugin core post types.
  */
 public static function get_ms_post_types()
 {
     $cpts = array(MS_Model_Membership::get_post_type(), MS_Model_Invoice::get_post_type(), MS_Model_Communication::get_post_type(), MS_Model_Relationship::get_post_type(), MS_Model_Event::get_post_type());
     return apply_filters('ms_rule_cptgroup_model_get_ms_post_types', $cpts);
 }
 /**
  * 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;
 }
 /**
  * Creates a subscription that starts immediately.
  *
  * @since  1.0.0
  * @internal
  *
  * @param  M2_Stripe_Customer $customer Stripe customer to charge.
  * @param  MS_Model_Invoice $invoice The relevant invoice.
  * @return M2_Stripe_Subscription The resulting charge object.
  */
 public function subscribe($customer, $invoice)
 {
     $membership = $invoice->get_membership();
     $plan_id = MS_Gateway_Stripeplan::get_the_id($membership->id, 'plan');
     $subscription = self::get_subscription($customer, $membership);
     /*
      * If no active subscription was found for the membership create it.
      */
     if (!$subscription) {
         $tax_percent = null;
         $coupon_id = null;
         if (is_numeric($invoice->tax_rate) && $invoice->tax_rate > 0) {
             $tax_percent = floatval($invoice->tax_rate);
         }
         if ($invoice->coupon_id) {
             $coupon_id = MS_Gateway_Stripeplan::get_the_id($invoice->coupon_id, 'coupon');
         }
         $args = array('plan' => $plan_id, 'tax_percent' => $tax_percent, 'coupon' => $coupon_id);
         $subscription = $customer->subscriptions->create($args);
     }
     return apply_filters('ms_gateway_stripe_subscribe', $subscription, $customer, $invoice, $membership, $this);
 }
 public function get_views()
 {
     $all_status = MS_Model_Invoice::get_status_types();
     $views = array();
     $args = $this->get_query_args();
     if (isset($args['meta_query']) && isset($args['meta_query']['status'])) {
         unset($args['meta_query']['status']);
     }
     $url = esc_url_raw(remove_query_arg(array('status', 'msg')));
     $count = MS_Model_Invoice::get_invoice_count($args);
     $views['all'] = array('url' => $url, 'label' => __('All', MS_TEXT_DOMAIN), 'count' => $count);
     $url = esc_url_raw(add_query_arg('status', 'open', remove_query_arg(array('status', 'msg'))));
     $args = $this->get_query_args();
     $args['meta_query']['status']['value'] = array(MS_Model_Invoice::STATUS_BILLED, MS_Model_Invoice::STATUS_PENDING);
     $args['meta_query']['status']['compare'] = 'IN';
     $count = MS_Model_Invoice::get_invoice_count($args);
     $views['open'] = array('url' => $url, 'label' => __('Billed or Pending', MS_TEXT_DOMAIN), 'count' => $count);
     foreach ($all_status as $status => $desc) {
         if ('billed' == $status) {
             continue;
         }
         if ('pending' == $status) {
             continue;
         }
         $args = $this->get_query_args();
         $args['meta_query']['status']['value'] = $status;
         $count = MS_Model_Invoice::get_invoice_count($args);
         $status_url = esc_url_raw(add_query_arg(array('status' => $status), remove_query_arg(array('msg'))));
         $views[$status] = array('url' => $status_url, 'label' => $desc, 'count' => $count);
     }
     return apply_filters('ms_helper_listtable_billing_views', $views);
 }
 /**
  * Get a list of all invoices linked to this relationship
  *
  * @since  1.0.0
  * @api
  *
  * @return MS_Model_Invoice[] List of invoices.
  */
 public function get_invoices()
 {
     $invoices = MS_Model_Invoice::get_invoices(array('nopaging' => true, 'meta_query' => array(array('key' => 'ms_relationship_id', 'value' => $this->id))));
     return apply_filters('ms_model_relationship_get_invoices', $invoices);
 }
 public function get_views()
 {
     $all_status = array(MS_Model_Invoice::STATUS_PAID => __('Paid', 'membership2'), MS_Model_Invoice::STATUS_NEW => __('Draft', 'membership2'), MS_Model_Invoice::STATUS_DENIED => __('Denied', 'membership2'));
     $views = array();
     $orig_status = '';
     if (isset($_REQUEST['status'])) {
         $orig_status = $_REQUEST['status'];
     }
     $_REQUEST['status'] = 'default';
     $args = $this->get_query_args();
     $_REQUEST['status'] = $orig_status;
     $base_url = esc_url_raw(remove_query_arg(array('status', 'msg')));
     $count = MS_Model_Invoice::get_invoice_count($args);
     $views['all'] = array('url' => $base_url, 'label' => __('Default', 'membership2'), 'count' => $count);
     $url = esc_url_raw(add_query_arg('status', 'open', $base_url));
     $args = $this->get_query_args();
     $args['meta_query']['status']['value'] = array(MS_Model_Invoice::STATUS_BILLED, MS_Model_Invoice::STATUS_PENDING);
     $args['meta_query']['status']['compare'] = 'IN';
     $count = MS_Model_Invoice::get_invoice_count($args);
     $views['open'] = array('url' => $url, 'label' => __('Billed or Pending', 'membership2'), 'count' => $count);
     foreach ($all_status as $status => $desc) {
         $args = $this->get_query_args();
         $args['meta_query']['status']['value'] = $status;
         $count = MS_Model_Invoice::get_invoice_count($args);
         if ($count) {
             $status_url = esc_url_raw(add_query_arg(array('status' => $status), remove_query_arg(array('msg'))));
         } else {
             $status_url = false;
             $desc .= ' (0)';
             if (MS_Model_Invoice::STATUS_DENIED != $status) {
                 $desc .= ' |';
             }
             $count = false;
         }
         $views[$status] = array('url' => $status_url, 'label' => $desc, 'count' => $count);
     }
     return apply_filters('ms_helper_listtable_billing_views', $views);
 }
 /**
  * Called by MS_Model_Invoice before a new invoice is saved. We apply the
  * coupon discount to the total amount, if a coupon was used.
  *
  * @since  1.0.0
  * @param  MS_Model_Invoice $invoice
  * @param  MS_Model_Relationship $subscription
  * @return MS_Model_Invoice
  */
 public function apply_discount($invoice, $subscription)
 {
     $membership = $subscription->get_membership();
     $member = MS_Factory::load('MS_Model_Member', $subscription->user_id);
     if (isset($_POST['apply_coupon_code'])) {
         $coupon = apply_filters('ms_addon_coupon_model', MS_Addon_Coupon_Model::load_by_code($_POST['coupon_code']));
         $coupon->save_application($subscription);
     } else {
         $coupon = MS_Addon_Coupon_Model::get_application($member->id, $membership->id);
         if (!empty($_POST['remove_coupon_code'])) {
             $note = sprintf(__('Remove Coupon "%s"', MS_TEXT_DOMAIN), $coupon->code);
             $invoice->add_notes($note);
             $coupon->remove_application($member->id, $membership->id);
             $coupon = false;
         }
     }
     self::the_coupon($coupon);
     if ($coupon && $coupon->is_valid($membership->id)) {
         $discount = $coupon->get_discount_value($subscription);
         $invoice->coupon_id = $coupon->id;
         $invoice->discount = $discount;
         $note = sprintf(__('Apply Coupon "%s": Discount %s %s!', MS_TEXT_DOMAIN), $coupon->code, $invoice->currency, $discount);
         $invoice->add_notes($note);
     } else {
         $invoice->coupon_id = '';
         $invoice->discount = 0;
     }
     return $invoice;
 }
 /**
  * Processes online payments.
  *
  * Send to Authorize.net to process the payment immediatly.
  *
  * @since  1.0.0
  * @param MS_Model_Invoice $invoice The invoice to pay.
  * @param MS_Model_Member The member paying the invoice.
  * @return bool True on success, otherwise throws an exception.
  */
 protected function online_purchase(&$invoice, $member, $log_action)
 {
     $success = false;
     $notes = '';
     $amount = 0;
     $subscription = $invoice->get_subscription();
     do_action('ms_gateway_authorize_online_purchase_before', $invoice, $member, $this);
     $need_code = lib3()->is_true($this->secure_cc);
     $have_code = !empty($_POST['card_code']);
     if (0 == $invoice->total) {
         $notes = __('Total is zero. Payment approved. Not sent to gateway.', 'membership2');
         $invoice->pay_it(MS_Gateway_Free::ID, '');
         $invoice->add_notes($notes);
         $invoice->save();
         $invoice->changed();
     } elseif (!$need_code || $have_code) {
         $amount = MS_Helper_Billing::format_price($invoice->total);
         $cim_transaction = $this->get_cim_transaction($member);
         $cim_transaction->amount = $amount;
         $cim_transaction->order->invoiceNumber = $invoice->id;
         $invoice->timestamp = time();
         $invoice->save();
         $_POST['API Out: Secure Payment'] = lib3()->is_true($this->secure_cc);
         $_POST['API Out: CustomerProfileID'] = $cim_transaction->customerProfileId;
         $_POST['API Out: PaymentProfileID'] = $cim_transaction->customerPaymentProfileId;
         $_POST['API Out: InvoiceNumber'] = $cim_transaction->order->invoiceNumber;
         $response = $this->get_cim()->createCustomerProfileTransaction('AuthCapture', $cim_transaction);
         if (!empty($response->xml) && !empty($response->xml->messages) && !empty($response->xml->messages->message)) {
             $msg = $response->xml->messages->message;
             $_POST['API Response: Short'] = $msg->code . ': ' . $msg->text;
         } else {
             $_POST['API Response: Short'] = '-';
         }
         if (isset($response->response)) {
             if (is_string($response->response)) {
                 $_POST['API Response: XML'] = $response->response;
             } else {
                 $_POST['API Response: XML'] = json_encode($response->response);
             }
         } else {
             $_POST['API Response: XML'] = json_encode($response->response);
         }
         if ($response->isOk()) {
             $transaction_response = $response->getTransactionResponse();
             if ($transaction_response->approved) {
                 $external_id = $response->getTransactionResponse()->transaction_id;
                 $invoice->pay_it($this->id, $external_id);
                 $success = true;
                 $notes = __('Payment successful', 'membership2');
             } else {
                 $notes = sprintf(__('Payment Failed: code %s, subcode %s, reason code %, reason %s', 'membership2'), $transaction_response->response_code, $transaction_response->response_subcode, $transaction_response->response_reason_code, $transaction_response->response_reason);
             }
         } else {
             $notes = __('Payment Failed: ', 'membership2') . $response->getMessageText();
         }
     } elseif ($need_code && !$have_code) {
         $notes = __('Secure payment failed: No Card Code found', 'membership2');
     }
     // Mask the credit card number before logging it to database.
     $card_num = '';
     $card_code = '';
     if (isset($_POST['card_num'])) {
         // Card Num   6789765435678765
         // Becomes    ************8765
         $card_num = str_replace(' ', '', $_POST['card_num']);
         $_POST['card_num'] = str_pad(substr($card_num, -4), strlen($card_num), '*', STR_PAD_LEFT);
     }
     if (isset($_POST['card_code'])) {
         $card_code = $_POST['card_code'];
         $_POST['card_code'] = str_repeat('*', strlen($card_code));
     }
     do_action('ms_gateway_transaction_log', self::ID, $log_action, $success, $subscription->id, $invoice->id, $amount, $notes, $external_id);
     // Restore the POST data in case it's used elsewhere.
     $_POST['card_num'] = $card_num;
     $_POST['card_code'] = $card_code;
     unset($_POST['API Out: CustomerProfileID']);
     unset($_POST['API Out: PaymentProfileID']);
     unset($_POST['API Out: InvoiceNumber']);
     unset($_POST['API Out: Secure Payment']);
     unset($_POST['API Response: Short']);
     unset($_POST['API Response: XML']);
     return $success;
 }
 /**
  * Processes online payments.
  *
  * Send to Authorize.net to process the payment immediatly.
  *
  * @since  1.0.0
  * @param MS_Model_Invoice $invoice The invoice to pay.
  * @param MS_Model_Member The member paying the invoice.
  * @return bool True on success, otherwise throws an exception.
  */
 protected function online_purchase(&$invoice, $member, $log_action)
 {
     $success = false;
     $notes = '';
     $amount = 0;
     $subscription = $invoice->get_subscription();
     do_action('ms_gateway_authorize_online_purchase_before', $invoice, $member, $this);
     if (0 == $invoice->total) {
         $notes = __('Total is zero. Payment approved. Not sent to gateway.', MS_TEXT_DOMAIN);
         $invoice->pay_it(MS_Gateway_Free::ID, '');
         $invoice->add_notes($notes);
         $invoice->save();
         $invoice->changed();
     } else {
         $amount = MS_Helper_Billing::format_price($invoice->total);
         $cim_transaction = $this->get_cim_transaction($member);
         $cim_transaction->amount = $amount;
         $cim_transaction->order->invoiceNumber = $invoice->id;
         $invoice->timestamp = time();
         $invoice->save();
         $response = $this->get_cim()->createCustomerProfileTransaction('AuthCapture', $cim_transaction);
         if ($response->isOk()) {
             $transaction_response = $response->getTransactionResponse();
             if ($transaction_response->approved) {
                 $external_id = $response->getTransactionResponse()->transaction_id;
                 $invoice->pay_it($this->id, $external_id);
                 $success = true;
                 $notes = __('Payment successful', MS_TEXT_DOMAIN);
             } else {
                 $notes = sprintf(__('Payment Failed: code %s, subcode %s, reason code %, reason %s', MS_TEXT_DOMAIN), $transaction_response->response_code, $transaction_response->response_subcode, $transaction_response->response_reason_code, $transaction_response->response_reason);
             }
         } else {
             $notes = __('Payment Failed: ', MS_TEXT_DOMAIN) . $response->getMessageText();
         }
     }
     do_action('ms_gateway_transaction_log', self::ID, $log_action, $success, $subscription->id, $invoice->id, $amount, $notes);
     return $success;
 }
 /**
  * Called right before the payment form on the front end is displayed.
  * We check if the user already specified an invitation code or not.
  *
  * If no code was specified then we remove all payment buttons and display
  * an input field for the invitation code instead.
  *
  * @since  1.0.1.0
  * @param  bool $flag The original Skip-Form flag.
  * @param  MS_Model_Invoice $invoice
  * @param  MS_View $view The parent view.
  * @return bool The modified Skip-Form flag.
  */
 public function do_not_skip($flag, $invoice, $view)
 {
     $membership = $invoice->get_membership();
     $is_public = lib3()->is_true($membership->get_custom_data('no_invitation'));
     if (!$is_public) {
         // Do not skip the form!
         $flag = false;
     }
     return $flag;
 }
 /**
  * When an invoice was paid we need to notify taxamo of the transaction.
  *
  * @since  1.0.0
  * @param  MS_Model_Invoice $invoice The processed invoice.
  */
 public function invoice_paid($invoice)
 {
     if (!$invoice->is_paid()) {
         return;
     }
     if (0 == $invoice->total) {
         return;
     }
     $membership = $invoice->get_membership();
     $member = $invoice->get_member();
     MS_Addon_Taxamo_Api::register_payment($invoice->total, $membership->name, $invoice->tax_rate, $invoice->get_invoice_number(), $member->full_name, $member->email, $invoice->gateway_id, $invoice->currency, $invoice->checkout_ip);
 }
 /**
  * Use a special template for our custom post types.
  *
  * Invoices:
  * Replaces the themes "Single" template with our invoice template when an
  * invoice is displayed. The theme can override this by defining its own
  * m2-invoice.php / single-ms_invoice.php template.
  *
  * You can even specifiy a membership ID in the page template to create
  * a custom invoice form based on the membership that is billed.
  * Example:
  *     m2-invoice-100.php (Invoice form for membership 100)
  *
  * @since  1.0.0
  * @see filter single_template
  *
  * @param string $template The template path to filter.
  * @return string The template path.
  */
 public function custom_single_template($default_template)
 {
     global $post;
     $template = '';
     // Checks for invoice single template.
     if ($post->post_type == MS_Model_Invoice::get_post_type()) {
         $invoice = MS_Factory::load('MS_Model_Invoice', $post->ID);
         // First look for themes 'm2-invoice-100.php' template (membership ID).
         $template = get_query_template('m2', 'm2-invoice-' . $invoice->membership_id . '.php');
         // Fallback to themes 'm2-invoice.php' template.
         if (!$template) {
             $template = get_query_template('m2', 'm2-invoice.php');
         }
         // Second look for themes 'single-ms_invoice.php' template.
         if (!$template && strpos($default_template, '/single-ms_invoice.php')) {
             $template = $default_template;
         }
         // Last: Use the default M2 invoice template.
         if (!$template) {
             $invoice_template = apply_filters('ms_controller_plugin_invoice_template', MS_Plugin::instance()->dir . 'app/template/single-ms_invoice.php');
             if (file_exists($invoice_template)) {
                 $template = $invoice_template;
             }
         }
     }
     if (!$template) {
         $template = $default_template;
     }
     return $template;
 }
 /**
  * Import specific data: A single invoice
  *
  * @since  1.0.0
  * @param  object $obj The import object
  */
 protected function import_invoice($subscription, $obj)
 {
     $ms_invoice = MS_Model_Invoice::create_invoice($subscription);
     $ms_invoice->invoice_number = $obj->invoice_number;
     $ms_invoice->external_id = $obj->external_id;
     $ms_invoice->gateway_id = $obj->gateway;
     $ms_invoice->status = $obj->status;
     $ms_invoice->coupon_id = $obj->coupon;
     $ms_invoice->currency = $obj->currency;
     $ms_invoice->amount = $obj->amount;
     $ms_invoice->discount = $obj->discount;
     $ms_invoice->pro_rate = $obj->discount2;
     $ms_invoice->total = $obj->total;
     $ms_invoice->trial_period = $obj->for_trial;
     $ms_invoice->due_date = $obj->due;
     $ms_invoice->notes = $obj->notes;
     // Remember where this invoice comes from.
     $ms_invoice->source = $this->source_key;
     $ms_invoice->save();
 }
Example #18
0
 /**
  * Register plugin custom post types.
  *
  * @since  1.0.0
  */
 public function register_custom_post_types()
 {
     do_action('ms_plugin_register_custom_post_types_before', $this);
     $cpts = apply_filters('ms_plugin_register_custom_post_types', array(MS_Model_Membership::get_post_type() => MS_Model_Membership::get_register_post_type_args(), MS_Model_Relationship::get_post_type() => MS_Model_Relationship::get_register_post_type_args(), MS_Model_Invoice::get_post_type() => MS_Model_Invoice::get_register_post_type_args(), MS_Model_Communication::get_post_type() => MS_Model_Communication::get_register_post_type_args(), MS_Model_Event::get_post_type() => MS_Model_Event::get_register_post_type_args()));
     foreach ($cpts as $cpt => $args) {
         MS_Helper_Utility::register_post_type($cpt, $args);
     }
 }
 /**
  * Membership account page shortcode callback function.
  *
  * @since  1.0.0
  *
  * @param mixed[] $atts Shortcode attributes.
  */
 public function membership_account($atts)
 {
     MS_Helper_Shortcode::did_shortcode(MS_Helper_Shortcode::SCODE_MS_ACCOUNT);
     $data = apply_filters('ms_controller_shortcode_membership_account_atts', shortcode_atts(array('show_membership' => true, 'show_membership_change' => true, 'membership_title' => __('Your Membership', 'membership2'), 'membership_change_label' => __('Change', 'membership2'), 'show_profile' => true, 'show_profile_change' => true, 'profile_title' => __('Personal details', 'membership2'), 'profile_change_label' => __('Edit', 'membership2'), 'show_invoices' => true, 'limit_invoices' => 10, 'show_all_invoices' => true, 'invoices_title' => __('Invoices', 'membership2'), 'invoices_details_label' => __('View all', 'membership2'), 'show_activity' => true, 'limit_activities' => 10, 'show_all_activities' => true, 'activity_title' => __('Activities', 'membership2'), 'activity_details_label' => __('View all', 'membership2')), $atts));
     $data['show_membership'] = lib3()->is_true($data['show_membership']);
     $data['show_membership_change'] = lib3()->is_true($data['show_membership_change']);
     $data['show_profile'] = lib3()->is_true($data['show_profile']);
     $data['show_profile_change'] = lib3()->is_true($data['show_profile_change']);
     $data['show_invoices'] = lib3()->is_true($data['show_invoices']);
     $data['show_all_invoices'] = lib3()->is_true($data['show_all_invoices']);
     $data['show_activity'] = lib3()->is_true($data['show_activity']);
     $data['show_all_activities'] = lib3()->is_true($data['show_all_activities']);
     $data['limit_invoices'] = absint($data['limit_invoices']);
     $data['limit_activities'] = absint($data['limit_activities']);
     $data['member'] = MS_Model_Member::get_current_member();
     $data['membership'] = array();
     $subscriptions = MS_Model_Relationship::get_subscriptions(array('user_id' => $data['member']->id, 'status' => 'all'));
     if (is_array($subscriptions)) {
         foreach ($subscriptions as $subscription) {
             // Do not display system-memberships in Account
             if ($subscription->is_system()) {
                 continue;
             }
             // Do not display deactivated memberships in Account
             if ($subscription->get_status() == MS_Model_Relationship::STATUS_DEACTIVATED) {
                 continue;
             }
             $data['subscription'][] = $subscription;
         }
     }
     $data['invoices'] = MS_Model_Invoice::get_public_invoices($data['member']->id, $data['limit_invoices']);
     $data['events'] = MS_Model_Event::get_events(array('author' => $data['member']->id, 'posts_per_page' => $data['limit_activities']));
     $view = MS_Factory::create('MS_View_Shortcode_Account');
     $view->data = apply_filters('ms_view_shortcode_account_data', $data, $this);
     return $view->to_html();
 }
 /**
  * Manage user account actions.
  *
  * @since  1.0.0
  * @internal
  */
 public function user_account_manager()
 {
     $action = $this->get_action();
     $member = MS_Model_Member::get_current_member();
     /**
      * These actions are always executed when any user account page loads.
      *
      * @since  1.0.1.0
      */
     do_action('ms_frontend_user_account_manager-' . $action, $this);
     do_action('ms_frontend_user_account_manager', $action, $this);
     if ($this->verify_nonce()) {
         /**
          * The following two actions are only executed when a form was
          * submitted on a user account page.
          *
          * @since  1.0.1.0
          */
         do_action('ms_frontend_user_account_manager_submit-' . $action, $this);
         do_action('ms_frontend_user_account_manager_submit', $action, $this);
     }
     switch ($action) {
         case self::ACTION_EDIT_PROFILE:
             $data = array();
             if ($this->verify_nonce()) {
                 if (is_array($_POST)) {
                     foreach ($_POST as $field => $value) {
                         $member->{$field} = $value;
                     }
                 }
                 try {
                     $member->validate_member_info();
                     $member->save();
                     wp_safe_redirect(esc_url_raw(remove_query_arg('action')));
                     exit;
                 } catch (Exception $e) {
                     $data['errors'] = $e->getMessage();
                 }
             }
             $view = MS_Factory::create('MS_View_Frontend_Profile');
             $data['member'] = $member;
             $data['action'] = $action;
             $view->data = apply_filters('ms_view_frontend_profile_data', $data, $this);
             $view->add_filter('the_content', 'to_html', 1);
             break;
         case self::ACTION_VIEW_INVOICES:
             $data['invoices'] = MS_Model_Invoice::get_public_invoices($member->id);
             $view = MS_Factory::create('MS_View_Frontend_Invoices');
             $view->data = apply_filters('ms_view_frontend_frontend_invoices', $data, $this);
             $view->add_filter('the_content', 'to_html', 1);
             break;
         case self::ACTION_VIEW_ACTIVITIES:
             $data['events'] = MS_Model_Event::get_events(array('author' => $member->id, 'posts_per_page' => -1));
             $view = MS_Factory::create('MS_View_Frontend_Activities');
             $view->data = apply_filters('ms_view_frontend_frontend_activities', $data, $this);
             $view->add_filter('the_content', 'to_html', 1);
             break;
         case self::ACTION_VIEW_RESETPASS:
             /**
              * Reset password action.
              * This action is accessed via the password-reset email
              * @see  class-ms-controller-dialog.php
              *
              * The action is targeted to the Account-page but actually calls
              * the Login-Shortcode.
              */
             $view = MS_Factory::create('MS_View_Shortcode_Login');
             $view->data = array('action' => 'resetpass');
             $view->add_filter('the_content', 'to_html', 1);
             break;
         default:
             // Do nothing...
             break;
     }
 }
 /**
  * Calculate the membership status.
  *
  * Calculate status for the membership verifying the start date,
  * trial exire date and expire date.
  *
  * @since  1.0.0
  * @internal
  *
  * @param string $set_status The set status to compare.
  * @return string The calculated status.
  */
 protected function calculate_status($set_status = null, $debug = false)
 {
     /**
      * Documented in check_membership_status()
      *
      * @since  1.0.0
      */
     if (MS_Plugin::get_modifier('MS_LOCK_SUBSCRIPTIONS')) {
         return $set_status;
     }
     $membership = $this->get_membership();
     $calc_status = null;
     $debug_msg = array();
     $check_trial = $this->is_trial_eligible();
     if (!empty($this->payments)) {
         /*
          * The user already paid for this membership, so don't check for
          * trial status anymore
          */
         $check_trial = false;
     }
     // If the start-date is not reached yet, then set membership to Pending.
     if (!$calc_status && !empty($this->start_date) && strtotime($this->start_date) > strtotime(MS_Helper_Period::current_date())) {
         $calc_status = self::STATUS_WAITING;
         $debug_msg[] = '[WAITING: Start-date not reached]';
     } elseif (!$calc_status && $debug) {
         $debug_msg[] = '[Not WAITING: No start-date or start-date reached]';
     }
     if ($check_trial) {
         if (!$calc_status && strtotime($this->trial_expire_date) >= strtotime(MS_Helper_Period::current_date())) {
             $calc_status = self::STATUS_TRIAL;
             $debug_msg[] = '[TRIAL: Trial-Expire date not reached]';
         } elseif (!$calc_status && $debug) {
             $debug_msg[] = '[Not TRIAL: Trial-Expire date reached]';
         }
         if (!$calc_status && strtotime($this->trial_expire_date) < strtotime(MS_Helper_Period::current_date())) {
             $calc_status = self::STATUS_TRIAL_EXPIRED;
             $debug_msg[] = '[TRIAL-EXPIRED: Trial-Expire date reached]';
         } elseif (!$calc_status && $debug) {
             $debug_msg[] = '[Not TRIAL-EXPIRED: Trial-Expire date not reached]';
         }
     } elseif (!$calc_status && $debug) {
         $debug_msg[] = '[Skipped TRIAL status]';
     }
     // Status an only become active when added by admin or invoice is paid.
     $can_activate = false;
     if ('admin' == $this->gateway_id) {
         $can_activate = true;
         $debug_msg[] = '[Can activate: Admin gateway]';
     } elseif ($membership->is_free()) {
         $can_activate = true;
         $debug_msg[] = '[Can activate: Free membership]';
     } elseif (!empty($this->source)) {
         $can_activate = true;
         $debug_msg[] = '[Can activate: Imported subscription]';
     } else {
         $valid_payment = false;
         // Check if there is *any* payment, no matter what height.
         foreach ($this->get_payments() as $payment) {
             if ($payment['amount'] > 0) {
                 $valid_payment = true;
                 $debug_msg[] = '[Can activate: Payment found]';
                 break;
             }
         }
         if (!$valid_payment) {
             // Check if any invoice was paid already.
             for ($ind = $this->current_invoice_number; $ind > 0; $ind -= 1) {
                 $invoice = MS_Model_Invoice::get_invoice($this->id, $ind);
                 if (!$invoice) {
                     continue;
                 }
                 if ($invoice->uses_trial) {
                     continue;
                 }
                 if ($invoice->is_paid()) {
                     $valid_payment = true;
                     $debug_msg[] = '[Can activate: Paid invoice found]';
                     break;
                 }
             }
         }
         if (!$valid_payment) {
             // Check if the current invoice is free.
             $invoice = $this->get_current_invoice();
             if (0 == $invoice->total) {
                 $valid_payment = true;
             }
         }
         if ($valid_payment) {
             $can_activate = true;
         }
         if (!$can_activate && $debug) {
             $debug_msg[] = sprintf('[Can not activate: Gateway: %s; Price: %s; Invoice: %s]', $this->gateway_id, $membership->price, $invoice->total);
         }
     }
     if ($can_activate) {
         // Permanent memberships grant instant access, no matter what.
         if (!$calc_status && MS_Model_Membership::PAYMENT_TYPE_PERMANENT == $membership->payment_type) {
             $calc_status = self::STATUS_ACTIVE;
             $debug_msg[] = '[ACTIVE(1): Payment-type is permanent]';
         } elseif (!$calc_status && $debug) {
             $debug_msg[] = '[Not ACTIVE(1): Payment-type is not permanent]';
         }
         // If expire date is empty and Active-state is requests then use active.
         if (!$calc_status && empty($this->expire_date) && self::STATUS_ACTIVE == $set_status) {
             $calc_status = self::STATUS_ACTIVE;
             $debug_msg[] = '[ACTIVE(2): Expire date empty and active requested]';
         } elseif (!$calc_status && $debug) {
             $debug_msg[] = '[Not ACTIVE(2): Expire date set or wrong status-request]';
         }
         // If expire date is not reached then membership obviously is active.
         if (!$calc_status && !empty($this->expire_date) && strtotime($this->expire_date) >= strtotime(MS_Helper_Period::current_date())) {
             $calc_status = self::STATUS_ACTIVE;
             $debug_msg[] = '[ACTIVE(3): Expire date set and not reached]';
         } elseif (!$calc_status && $debug) {
             $debug_msg[] = '[Not ACTIVE(3): Expire date set and reached]';
         }
     } elseif (!$calc_status && self::STATUS_PENDING == $this->status) {
         // Invoice is not paid yet.
         $calc_status = self::STATUS_PENDING;
         $debug_msg[] = '[PENDING: Cannot activate pending subscription]';
     } elseif (!$calc_status && $debug) {
         $debug_msg[] = '[Not ACTIVE/PENDING: Cannot activate subscription]';
     }
     // If no other condition was true then the expire date was reached.
     if (!$calc_status) {
         $calc_status = self::STATUS_EXPIRED;
         $debug_msg[] = '[EXPIRED: Default status]';
     }
     // Did the user cancel the membership?
     $cancel_it = self::STATUS_CANCELED == $set_status || self::STATUS_CANCELED == $this->status && self::STATUS_ACTIVE != $set_status && self::STATUS_TRIAL != $set_status;
     if ($cancel_it) {
         /*
          * When a membership is cancelled then it will stay "Cancelled"
          * until the expiration date is reached. A user has access to the
          * contents of a cancelled membership until it expired.
          */
         if (self::STATUS_EXPIRED == $calc_status) {
             // Membership has expired. Finally deactivate it!
             // (possibly it was cancelled a few days earlier)
             $calc_status = self::STATUS_DEACTIVATED;
         } elseif (self::STATUS_TRIAL_EXPIRED == $calc_status) {
             // Trial period has expired. Finally deactivate it!
             $calc_status = self::STATUS_DEACTIVATED;
         } elseif (self::STATUS_TRIAL == $calc_status) {
             // User can keep access until trial period finishes...
             $calc_status = self::STATUS_CANCELED;
         } elseif (MS_Model_Membership::PAYMENT_TYPE_PERMANENT == $membership->payment_type) {
             // This membership has no expiration-time. Deactivate it!
             $calc_status = self::STATUS_DEACTIVATED;
         } elseif (self::STATUS_WAITING == $calc_status) {
             // The membership did not yet start. Deactivate it!
             $calc_status = self::STATUS_DEACTIVATED;
         } elseif (!$this->expire_date) {
             // Membership without expire date cannot be cancelled. Deactivate it!
             $calc_status = self::STATUS_DEACTIVATED;
         } else {
             // Wait until the expiration date is reached...
             $calc_status = self::STATUS_CANCELED;
         }
     }
     if ($debug) {
         // Intended debug output, leave it here.
         lib3()->debug->dump($debug_msg);
     }
     return apply_filters('membership_model_relationship_calculate_status', $calc_status, $this);
 }