/** * Check for card expiration date. * * Save event for card expire soon. * * @since 1.0.0 * * @access protected * @param MS_Model_Relationship $subscription The membership relationship. */ public function check_card_expiration($subscription) { do_action('ms_gateway_check_card_expiration_before', $this); $member = MS_Factory::load('MS_Model_Member', $subscription->user_id); $card_exp = $member->get_gateway_profile($this->id, 'card_exp'); if (!empty($card_exp)) { $comm = MS_Model_Communication::get_communication(MS_Model_Communication::COMM_TYPE_CREDIT_CARD_EXPIRE); $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); $card_expire_days = MS_Helper_Period::subtract_dates($card_exp, MS_Helper_Period::current_date(), DAY_IN_SECONDS, true); if ($card_expire_days < 0 || $days == $card_expire_days) { MS_Model_Event::save_event(MS_Model_Event::TYPE_CREDIT_CARD_EXPIRE, $subscription); } } do_action('ms_gateway_check_card_expiration_after', $this, $subscription); }
public function available_content_panel_data() { $relative = array(); $absolute = array(); $membership = $this->data['membership']; $rule_types = MS_Model_Rule::get_dripped_rule_types(); foreach ($rule_types as $rule_type) { $rule = $membership->get_rule($rule_type); $contents = (array) $rule->get_contents(); foreach ($contents as $content) { if ($rule->has_dripped_rules($content->id)) { $infos = $rule->dripped[$content->id]; $key = false; $content->date = $rule->get_dripped_description($content->id); $content->icon = 'visibility'; switch ($infos['type']) { case MS_Model_Rule::DRIPPED_TYPE_FROM_REGISTRATION: $content->icon = 'clock'; $key = MS_Helper_Period::get_period_in_days($infos['delay_unit'], $infos['delay_type']); $key = 100000 + $key * 1000; while (isset($relative[$key])) { $key += 1; } $relative[$key] = $content; break; case MS_Model_Rule::DRIPPED_TYPE_SPEC_DATE: $key = preg_replace('/[^0-9]/', '', $infos['date']); if (!$rule->has_access($content->id)) { $content->icon = 'lock'; } // Fall through // Fall through case MS_Model_Rule::DRIPPED_TYPE_INSTANTLY: default: if (empty($key)) { $key = 0; } $key = $key * 1000; while (isset($relative[$key])) { $key += 1; } $absolute[$key] = $content; break; } } } } ?> <div class="clear"></div> <div class="cf"> <div class="ms-half ms-available-soon space"> <div class="ms-bold"> <i class="dashicons dashicons-calendar ms-low"></i> <?php _e('Available on a specific date:', MS_TEXT_DOMAIN); ?> </div> <div class="inside"> <?php $this->content_box($absolute); ?> </div> </div> <div class="ms-half ms-available"> <div class="ms-bold"> <i class="dashicons dashicons-clock ms-low"></i> <?php _e('Relative to registration:', MS_TEXT_DOMAIN); ?> </div> <div class="inside"> <?php $this->content_box($relative); ?> </div> </div> </div> <div class="cf"> <div class="ms-half"> <div class="inside"> <div class="ms-protection-edit-wrapper"> <?php $edit_url = MS_Controller_Plugin::get_admin_url('protection', array('tab' => $rule->rule_type, 'membership_id' => $membership->id)); MS_Helper_Html::html_element(array('id' => 'edit_dripped', 'type' => MS_Helper_Html::TYPE_HTML_LINK, 'value' => __('Edit Dripped Content', MS_TEXT_DOMAIN), 'url' => $edit_url, 'class' => 'wpmui-field-button button')); if (!$membership->is_free) { $payment_url = esc_url_raw(add_query_arg(array('step' => MS_Controller_Membership::STEP_PAYMENT, 'edit' => 1))); MS_Helper_Html::html_element(array('id' => 'setup_payment', 'type' => MS_Helper_Html::TYPE_HTML_LINK, 'value' => __('Payment Options', MS_TEXT_DOMAIN), 'url' => $payment_url, 'class' => 'wpmui-field-button button')); } ?> </div> </div> </div> </div> <?php }
/** * Check membership status. * * Execute actions when time/period condition are met. * E.g. change membership status, add communication to queue, create invoices. * * This check is called via a cron job. * * @since 1.0.0 * @internal Used by Cron * @see MS_Model_Plugin::check_membership_status() */ public function check_membership_status() { do_action('ms_model_relationship_check_membership_status_before', $this); /** * Use `define( 'MS_LOCK_SUBSCRIPTIONS', true );` in wp-config.php to prevent * Membership2 from sending *any* emails to users. * Also any currently enqueued message is removed from the queue * * @since 1.0.0 */ if (MS_Plugin::get_modifier('MS_LOCK_SUBSCRIPTIONS')) { return false; } $membership = $this->get_membership(); $remaining_days = $this->get_remaining_period(); $remaining_trial_days = $this->get_remaining_trial_period(); $comms = MS_Model_Communication::get_communications($membership); $invoice_before_days = 5; //@todo create a setting to configure this period. $deactivate_expired_after_days = 30; //@todo create a setting to configure this period. $deactivate_pending_after_days = 30; //@todo create a setting to configure this period. $deactivate_trial_expired_after_days = 5; //@todo create a setting to configure this period. //@todo: Add a flag to subscriptions with sent communications. Then improve the conditions below to prevent multiple emails. do_action('ms_check_membership_status-' . $this->status, $this, $remaining_days, $remaining_trial_days); // Update the Subscription status. $next_status = $this->calculate_status(null); switch ($next_status) { case self::STATUS_TRIAL: if (MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_TRIAL) && MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_AUTO_MSGS_PLUS)) { // Send trial end communication. $comm = $comms[MS_Model_Communication::COMM_TYPE_BEFORE_TRIAL_FINISHES]; if ($comm->enabled) { $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); //@todo: This will send out the reminder multiple times on the reminder-day (4 times or more often) if ($days == $remaining_trial_days) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_BEFORE_TRIAL_FINISHES, $this); } } } // Check for card expiration $gateway = $this->get_gateway(); $gateway->check_card_expiration($this); break; case self::STATUS_TRIAL_EXPIRED: if (MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_TRIAL)) { // Mark the trial period as completed. $this->save() is below. $this->trial_period_completed = true; // Request payment to the gateway (for gateways that allows it). $gateway = $this->get_gateway(); /* * The subscription will be either automatically activated * or set to pending. * * Important: Set trial_period_completed=true before calling * request_payment()! */ if ($gateway->request_payment($this)) { $next_status = self::STATUS_ACTIVE; $this->status = $next_status; $this->config_period(); // Needed because of status change. } // Check for card expiration $gateway->check_card_expiration($this); // Deactivate expired memberships after a period of time. if ($deactivate_trial_expired_after_days < -$remaining_trial_days) { $this->deactivate_membership(); } } break; case self::STATUS_ACTIVE: case self::STATUS_EXPIRED: case self::STATUS_CANCELED: /* * Make sure the expire date has a correct value, in case the user * changed the payment_type of the parent membership after this * subscription was created. */ if ($this->payment_type != $membership->payment_type) { $this->payment_type = $membership->payment_type; switch ($this->payment_type) { case MS_Model_Membership::PAYMENT_TYPE_PERMANENT: $this->expire_date = false; break; default: // Either keep the current expire date (if valid) or // calculate a new expire date, based on current date. if (!$this->expire_date) { $this->expire_date = $this->calc_expire_date(MS_Helper_Period::current_date()); } break; } // Recalculate the days until the subscription expires. $remaining_days = $this->get_remaining_period(); // Recalculate the new Subscription status. $next_status = $this->calculate_status(); } /* * Only "Recurring" memberships will ever try to automatically * renew the subscription. All other types will expire when the * end date is reached. */ $auto_renew = $membership->payment_type == MS_Model_Membership::PAYMENT_TYPE_RECURRING; $deactivate = false; $invoice = null; if ($auto_renew && $membership->pay_cycle_repetitions > 0) { /* * The membership has a payment-repetition limit. * When this limit is reached then we do not auto-renew the * subscription but expire it. */ $payments = $this->get_payments(); if (count($payments) >= $membership->pay_cycle_repetitions) { $auto_renew = false; } } if ($auto_renew) { if ($remaining_days < $invoice_before_days) { // Create a new invoice. $invoice = $this->get_next_invoice(); } else { $invoice = $this->get_current_invoice(); } } else { $invoice = $this->get_current_invoice(); } // Advanced communications Add-on. if (MS_Model_Addon::is_enabled(MS_Model_Addon::ADDON_AUTO_MSGS_PLUS)) { // Before finishes communication. $comm = $comms[MS_Model_Communication::COMM_TYPE_BEFORE_FINISHES]; $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); if ($days == $remaining_days) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_BEFORE_FINISHES, $this); } // After finishes communication. $comm = $comms[MS_Model_Communication::COMM_TYPE_AFTER_FINISHES]; $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); if ($remaining_days < 0 && $days == abs($remaining_days)) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_AFTER_FINISHES, $this); } // Before payment due. $comm = $comms[MS_Model_Communication::COMM_TYPE_BEFORE_PAYMENT_DUE]; $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); $invoice_days = MS_Helper_Period::subtract_dates($invoice->due_date, MS_Helper_Period::current_date()); if (MS_Model_Invoice::STATUS_BILLED == $invoice->status && $days == $invoice_days) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_PAYMENT_BEFORE_DUE, $this); } // After payment due event $comm = $comms[MS_Model_Communication::COMM_TYPE_AFTER_PAYMENT_DUE]; $days = MS_Helper_Period::get_period_in_days($comm->period['period_unit'], $comm->period['period_type']); $invoice_days = MS_Helper_Period::subtract_dates($invoice->due_date, MS_Helper_Period::current_date()); if (MS_Model_Invoice::STATUS_BILLED == $invoice->status && $days == $invoice_days) { $comm->add_to_queue($this->id); MS_Model_Event::save_event(MS_Model_Event::TYPE_PAYMENT_AFTER_DUE, $this); } } // -- End of advanced communications Add-on // Subscription ended. See if we can renew it. if ($remaining_days <= 0) { if ($auto_renew) { /* * The membership can be renewed. Try to renew it * automatically by requesting the next payment from the * payment gateway (only works if gateway supports this) */ $gateway = $this->get_gateway(); $gateway->check_card_expiration($this); $gateway->request_payment($this); // Check if the payment was successful. $remaining_days = $this->get_remaining_period(); } /* * User did not renew the membership. Give him some time to * react before restricting his access. */ if ($deactivate_expired_after_days < -$remaining_days) { $deactivate = true; } } $next_status = $this->calculate_status(null); /* * When the subscription expires the first time then create a * new event that triggers the "Expired" email. */ if (self::STATUS_EXPIRED == $next_status && $next_status != $this->status) { MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_EXPIRED, $this); } elseif ($deactivate) { $this->deactivate_membership(); $next_status = $this->status; // Move membership to configured membership. $membership = $this->get_membership(); $new_membership = MS_Factory::load('MS_Model_Membership', $membership->on_end_membership_id); if ($new_membership->is_valid()) { $member = MS_Factory::load('MS_Model_Member', $this->user_id); $new_subscription = $member->add_membership($membership->on_end_membership_id, $this->gateway_id); MS_Model_Event::save_event(MS_Model_Event::TYPE_MS_MOVED, $new_subscription); /* * If the new membership is paid we want that the user * confirms the payment in his account. So we set it * to "Pending" first. */ if (!$new_membership->is_free()) { $new_subscription->status = self::STATUS_PENDING; } } } break; case self::STATUS_DEACTIVATED: /* * A subscription was finally deactivated. * Lets check if the member has any other active subscriptions, * or (if not) his account should be deactivated. * * First get a list of all subscriptions that do not have status * Pending / Deactivated. */ $subscriptions = self::get_subscriptions(array('user_id' => $this->user_id)); // Check if there is a subscription that keeps the user active. $deactivate = true; foreach ($subscriptions as $item) { if ($item->id == $this->id) { continue; } $deactivate = false; } if ($deactivate) { $member = $this->get_member(); $member->is_member = false; $member->save(); } break; case self::STATUS_PENDING: default: // Do nothing. break; } // Save the new status. $this->status = $next_status; $this->save(); // Save the changed email queue. foreach ($comms as $comm) { $comm->save(); } do_action('ms_model_relationship_check_membership_status_after', $this); }
/** * Creates or updates a single payment plan on Stripe. * * This function is called when the gateway is activated and after a * membership was saved to database. * * @since 1.0.0 */ public function update_stripe_data_membership($membership) { if (!$this->active) { return false; } $this->_api->set_gateway($this); $plan_data = array('id' => self::get_the_id($membership->id, 'plan'), 'amount' => 0); if (!$membership->is_free() && $membership->payment_type == MS_Model_Membership::PAYMENT_TYPE_RECURRING) { // Prepare the plan-data for Stripe. $trial_days = null; if ($membership->has_trial()) { $trial_days = MS_Helper_Period::get_period_in_days($membership->trial_period_unit, $membership->trial_period_type); } $interval = 'day'; $max_count = 365; switch ($membership->pay_cycle_period_type) { case MS_Helper_Period::PERIOD_TYPE_WEEKS: $interval = 'week'; $max_count = 52; break; case MS_Helper_Period::PERIOD_TYPE_MONTHS: $interval = 'month'; $max_count = 12; break; case MS_Helper_Period::PERIOD_TYPE_YEARS: $interval = 'year'; $max_count = 1; break; } $interval_count = min($max_count, $membership->pay_cycle_period_unit); $settings = MS_Plugin::instance()->settings; $plan_data['amount'] = absint($membership->price * 100); $plan_data['currency'] = $settings->currency; $plan_data['name'] = $membership->name; $plan_data['interval'] = $interval; $plan_data['interval_count'] = $interval_count; $plan_data['trial_period_days'] = $trial_days; // Check if the plan needs to be updated. $serialized_data = json_encode($plan_data); $temp_key = substr('ms-stripe-' . $plan_data['id'], 0, 45); $temp_data = MS_Factory::get_transient($temp_key); if ($temp_data != $serialized_data) { MS_Factory::set_transient($temp_key, $serialized_data, HOUR_IN_SECONDS); $this->_api->create_or_update_plan($plan_data); } } }