/** * 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(); }
/** * 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); }