/** * Sort function used as second param by `uasort()` to sort a subscription * list by membership priority. * Memberships with equal priority are sorted alphabeically. * * @since 1.0.1.0 * @param MS_Model_Relationship $a * @param MS_Model_Relationship $b * @return int -1: a < b | 0: a = b | +1: a > b */ public static function sort_by_priority($a, $b) { $m1 = $a->get_membership(); $m2 = $b->get_membership(); if ($m1->priority == $m2->priority) { return $m1->name < $m2->name ? -1 : 1; } else { return $m1->priority - $m2->priority; } }
/** * Apply coupon to get discount. * * If trial period is enabled, the discount will be applied in the trial price (even if it is free). * If the membership price is free, the discount will be zero. * If discount is bigger than the price, the discount will be equal to the price. * * @since 1.0.0 * * @param MS_Model_Relationship $subscription The membership relationship to apply coupon. * @return float The discount value. */ public function get_discount_value($subscription) { $membership = $subscription->get_membership(); $price = $membership->price; // Excluding Tax $original_price = $price; $discount = 0; if ($this->is_valid($membership->id)) { $discount = $this->discount; if (self::TYPE_PERCENT == $this->discount_type) { $discount = $price * $discount / 100; } $price -= $discount; if ($price < 0) { $price = 0; } $discount = $original_price - $price; $this->coupon_message = sprintf(__('Coupon applied: %1$s', 'membership2'), $this->code); } return apply_filters('ms_addon_coupon_model_apply_discount', $discount, $membership, $this); }
/** * Checks if a subscription has reached the maximum paycycle repetitions. * If the last paycycle was paid then the subscription is cancelled. * * @since 1.0.0 * @internal Called by process_purchase() and request_payment() * * @param MS_Model_Relationship $subscription * @param M2_Stripe_Subscription $stripe_sub */ protected function cancel_if_done($subscription, $stripe_sub) { $membership = $subscription->get_membership(); if ($membership->pay_cycle_repetitions < 1) { return; } $payments = $subscription->payments; if (count($payments) < $membership->pay_cycle_repetitions) { return; } $stripe_sub->cancel(array('at_period_end' => true)); }
/** * Create invoice. * * Create a new invoice using the membership information. * * @since 1.0.0 * * @param MS_Model_Relationship $subscription The membership to create invoice for. * @param int $invoice_number Optional. The invoice number. * * @return object $invoice */ public static function create_invoice($subscription, $invoice_number = false) { $membership = $subscription->get_membership(); if (!MS_Model_Membership::is_valid_membership($membership->id)) { throw new Exception('Invalid Membership.'); } $invoice = null; $member = MS_Factory::load('MS_Model_Member', $subscription->user_id); $invoice_status = self::STATUS_NEW; $notes = null; if (empty($invoice_number)) { $invoice_number = $subscription->current_invoice_number; } $invoice = self::get_invoice($subscription->id, $invoice_number); // No existing invoice, create a new one. if (!$invoice || !$invoice->id) { $invoice = MS_Factory::create('MS_Model_Invoice'); $invoice = apply_filters('ms_model_invoice', $invoice); } // Update invoice info. $invoice->ms_relationship_id = $subscription->id; $invoice->gateway_id = $subscription->gateway_id; $invoice->status = $invoice_status; $invoice->invoice_date = MS_Helper_Period::current_date(); $invoice->membership_id = $membership->id; $invoice->currency = MS_Plugin::instance()->settings->currency; $invoice->user_id = $member->id; $invoice->name = apply_filters('ms_model_invoice_name', sprintf(__('Invoice for %s - %s', MS_TEXT_DOMAIN), $membership->name, $member->username)); $invoice->invoice_number = $invoice_number; $invoice->discount = 0; $invoice->notes = $notes; $invoice->amount = $membership->price; // Without taxes! // Check for trial period in the first period. if ($subscription->is_trial_eligible() && $invoice_number === $subscription->current_invoice_number) { $invoice->trial_price = $membership->trial_price; // Without taxes! $invoice->uses_trial = true; $invoice->trial_ends = $subscription->trial_expire_date; } $invoice->set_due_date(); $invoice = apply_filters('ms_model_invoice_create_before_save', $invoice, $subscription); $invoice->save(); // Refresh the tax-rate and payment description. $invoice->total_amount_changed(); $invoice->save(); return apply_filters('ms_model_relationship_create_invoice', $invoice, $subscription, $invoice_number); }
/** * 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; }
/** * Replace comm_vars with corresponding values. * * @since 1.0.0 * * @param MS_Model_Relationship $subscription The membership relationship to send message to. * @param MS_Model_Member $member The member object to get info from. * @return array { * Returns array of ( $var_name => $var_replace ). * * @type string $var_name The variable name to replace. * @type string $var_replace The variable corresponding replace string. * } */ public function get_comm_vars($subscription, $member) { $currency = MS_Plugin::instance()->settings->currency . ' '; $invoice = null; $membership = null; if ($subscription && $subscription instanceof MS_Model_Relationship) { // First try to fetch the current invoice. $invoice = $subscription->get_current_invoice(false); $prev_invoice = $subscription->get_previous_invoice(); // If no current invoice exists then fetch the previous invoice. if (empty($invoice)) { $invoice = $prev_invoice; } $membership = $subscription->get_membership(); } $comm_vars = apply_filters('ms_model_communication_comm_vars', $this->comm_vars, $this->type, $member, $subscription); $wp_user = $member->get_user(); foreach ($comm_vars as $key => $description) { $var_value = ''; switch ($key) { case self::COMM_VAR_BLOG_NAME: $var_value = get_option('blogname'); break; case self::COMM_VAR_BLOG_URL: $var_value = get_option('home'); break; case self::COMM_VAR_USERNAME: $var_value = $member->username; break; case self::COMM_VAR_PASSWORD: /** * $member->password is ONLY available in the same request * when the new user account was created! After this we only * have the encrypted password in the DB, and the plain-text * version will never be available again in code... * * @since 1.0.1.1 */ if (self::COMM_TYPE_SIGNUP == $this->type) { $var_value = $member->password; } break; case self::COMM_VAR_USER_DISPLAY_NAME: $var_value = $wp_user->display_name; break; case self::COMM_VAR_USER_FIRST_NAME: $var_value = $member->first_name; break; case self::COMM_VAR_USER_LAST_NAME: $var_value = $member->last_name; break; case self::COMM_VAR_NET_NAME: $var_value = get_site_option('site_name'); break; case self::COMM_VAR_NET_URL: $var_value = get_site_option('siteurl'); break; case self::COMM_VAR_MS_ACCOUNT_PAGE_URL: $var_value = sprintf('<a href="%s">%s</a>', MS_Model_Pages::get_page_url(MS_Model_Pages::MS_PAGE_ACCOUNT), __('account page', MS_TEXT_DOMAIN)); break; // Needs: $membership // Needs: $membership case self::COMM_VAR_MS_NAME: if ($membership && $membership->name) { $var_value = $membership->name; } break; // Needs: $invoice // Needs: $invoice case self::COMM_VAR_MS_INVOICE: if ($invoice) { if ($invoice->total > 0 || $invoice->uses_trial) { $attr = array('post_id' => $invoice->id, 'pay_button' => 0); $scode = MS_Factory::load('MS_Controller_Shortcode'); $var_value = $scode->membership_invoice($attr); } } break; // Needs: $subscription // Needs: $subscription case self::COMM_VAR_MS_REMAINING_DAYS: if ($subscription) { $days = $subscription->get_remaining_period(); $var_value = sprintf(__('%s day%s', MS_TEXT_DOMAIN), $days, abs($days) > 1 ? 's' : ''); } break; // Needs: $subscription // Needs: $subscription case self::COMM_VAR_MS_REMAINING_TRIAL_DAYS: if ($subscription) { $days = $subscription->get_remaining_trial_period(); $var_value = sprintf(__('%s day%s', MS_TEXT_DOMAIN), $days, abs($days) > 1 ? 's' : ''); } break; // Needs: $subscription // Needs: $subscription case self::COMM_VAR_MS_EXPIRY_DATE: if ($subscription) { $var_value = $subscription->expire_date; } break; } $comm_vars[$key] = apply_filters('ms_model_communication_send_message_comm_var-' . $key, $var_value, $this->type, $member, $subscription, $invoice); } return apply_filters('ms_model_communication_get_comm_vars', $comm_vars, $member); }
/** * A payment was received, award affiliate reward to the referrer. * * Whenever a Membership2 invoice is paid we try to find the referrer of * the member and award a reward to him according to the payment settings. * * This function uses the Membership2 hook `ms_invoice_paid` which is called * when a user does either * (1) sucessfully make a payment for a paid membership or * (2) successfully subscribe to a free membership. * * @since 1.0.0 * @param MS_Model_Invoice $invoice The invoice which was paid. * @param MS_Model_Relationship $subscription */ public function payment_processed($invoice, $subscription) { global $affiliate; // Used for communication with Affiliates plugin. global $blog_id, $site_id; // Used for logging. $user_id = $invoice->user_id; $membership = $subscription->get_membership(); $pay_once = defined('AFFILIATE_PAYONCE') && 'yes' == AFFILIATE_PAYONCE; $user_id_referrer = get_user_meta($user_id, 'affiliate_referred_by', true); if (empty($user_id_referrer)) { // We do not know who referred the user, don't pay a commission. return; } $affiliate_paid = get_user_meta($user_id, 'affiliate_paid', true); if ($pay_once && 'yes' == $affiliate_paid) { // The referrer already got a one-time commission, don't pay again. return; } $complete_records = $affiliate->get_complete_records($user_id_referrer, date('Ym'), array(self::AREA_KEY), $user_id); if (is_array($complete_records)) { // Make sure that this subscription was not commissioned before. foreach ($complete_records as $record) { $meta = maybe_unserialize($record->meta); if ($meta['subscription_id'] == $subscription->id) { // It seems this subscription was already commissioned. return; } } } // Okay, the referrer is entitiled to the commission! /* * Reward is the money that the user receives. * It is stored in cents/milli-percent. * I.e. '100' $ -> 1.00 $ and '100' % -> 1.00 % */ $reward = $this->get_value($membership); $type = $this->get_type($membership); switch ($type) { case 'inv': $base = $invoice->subtotal; // Invoice amount without taxes. $reward = $base * $reward / 100; break; case 'mem': $base = $membership->price; // Membership price setting. $reward = $base * $reward / 100; break; case 'abs': default: // Reward already has correct value. break; } $reward = round($reward, 2, PHP_ROUND_HALF_DOWN); // Note: lib2() used here is provided by the Membership2 plugin. $meta = array('subscription_id' => $subscription->id, 'invoice_id' => $invoice->id, 'gateway_id' => $invoice->gateway_id, 'transaction_id' => $invoice->external_id, 'blog_id' => $blog_id, 'site_id' => $site_id, 'current_user_id' => get_current_user_id(), 'REMOTE_URL' => $_SERVER['HTTP_REFERER'], 'LOCAL_URL' => lib2()->net->current_url(), 'IP' => lib2()->net->current_ip()->ip); do_action('affiliate_purchase', $user_id_referrer, $reward, self::AREA_KEY, $user_id, __('Membership2', 'affiliate'), $meta); if ($pay_once) { update_user_meta($user_id, 'affiliate_paid', 'yes'); } }
/** * Save invitation application. * * Saving the application to keep track of the application in gateway return. * Using INVITATION_REDEMPTION_TIME to expire invitation application. * * This is a non-static function, as it saves the current object! * * @since 1.0.0 * @param MS_Model_Relationship $subscription The subscription to apply the invitation. */ public function save_application($subscription) { // Don't save empty invitations. if (empty($this->code)) { return false; } $membership = $subscription->get_membership(); $time = apply_filters('ms_addon_invitation_model_save_application_redemption_time', self::INVITATION_REDEMPTION_TIME); // Grab the user account as we should be logged in by now. $user = MS_Model_Member::get_current_member(); $key = self::get_transient_name($user->id, $membership->id); $transient = apply_filters('ms_addon_invitation_model_transient_value', array('id' => $this->id, 'user_id' => $user->id, 'membership_id' => $membership->id, 'message' => $this->invitation_message)); MS_Factory::set_transient($key, $transient, $time); $this->save(); do_action('ms_addon_invitation_model_save_application', $subscription, $this); }
/** * Replace comm_vars with corresponding values. * * @since 1.0.0 * * @param MS_Model_Relationship $subscription The membership relationship to send message to. * @param WP_User $wp_user The wordpress user object to get info from. * @return array { * Returns array of ( $var_name => $var_replace ). * * @type string $var_name The variable name to replace. * @type string $var_replace The variable corresponding replace string. * } */ public function get_comm_vars($subscription, $wp_user) { $currency = MS_Plugin::instance()->settings->currency . ' '; $membership = $subscription->get_membership(); $invoice = null; // First try to fetch the current invoice. $invoice = $subscription->get_current_invoice(false); $prev_invoice = $subscription->get_previous_invoice(); // If no current invoice exists then fetch the previous invoice. if (empty($invoice)) { $invoice = $prev_invoice; } $comm_vars = apply_filters('ms_model_communication_comm_vars', $this->comm_vars); foreach ($comm_vars as $key => $description) { switch ($key) { case self::COMM_VAR_BLOG_NAME: $comm_vars[$key] = get_option('blogname'); break; case self::COMM_VAR_BLOG_URL: $comm_vars[$key] = get_option('home'); break; case self::COMM_VAR_USERNAME: $comm_vars[$key] = $wp_user->user_login; break; case self::COMM_VAR_USER_DISPLAY_NAME: $comm_vars[$key] = $wp_user->display_name; break; case self::COMM_VAR_USER_FIRST_NAME: $comm_vars[$key] = $wp_user->user_firstname; break; case self::COMM_VAR_USER_LAST_NAME: $comm_vars[$key] = $wp_user->user_lastname; break; case self::COMM_VAR_NET_NAME: $comm_vars[$key] = get_site_option('site_name'); break; case self::COMM_VAR_NET_URL: $comm_vars[$key] = get_site_option('siteurl'); break; case self::COMM_VAR_MS_NAME: if ($membership->name) { $comm_vars[$key] = $membership->name; } else { $comm_vars[$key] = ''; } break; case self::COMM_VAR_MS_INVOICE: if (isset($invoice) && ($invoice->total > 0 || $invoice->uses_trial)) { $attr = array('post_id' => $invoice->id, 'pay_button' => 0); $scode = MS_Factory::load('MS_Controller_Shortcode'); $comm_vars[$key] = $scode->membership_invoice($attr); } else { $comm_vars[$key] = ''; } break; case self::COMM_VAR_MS_ACCOUNT_PAGE_URL: $comm_vars[$key] = sprintf('<a href="%s">%s</a>', MS_Model_Pages::get_page_url(MS_Model_Pages::MS_PAGE_ACCOUNT), __('account page', MS_TEXT_DOMAIN)); break; case self::COMM_VAR_MS_REMAINING_DAYS: $days = $subscription->get_remaining_period(); $comm_vars[$key] = sprintf(__('%s day%s', MS_TEXT_DOMAIN), $days, abs($days) > 1 ? 's' : ''); break; case self::COMM_VAR_MS_REMAINING_TRIAL_DAYS: $days = $subscription->get_remaining_trial_period(); $comm_vars[$key] = sprintf(__('%s day%s', MS_TEXT_DOMAIN), $days, abs($days) > 1 ? 's' : ''); break; case self::COMM_VAR_MS_EXPIRY_DATE: $comm_vars[$key] = $subscription->expire_date; break; } $comm_vars[$key] = apply_filters('ms_model_communication_send_message_comm_var_' . $key, $comm_vars[$key], $subscription); } return apply_filters('ms_model_communication_get_comm_vars', $comm_vars); }