public function column_due_date($item, $column_name) { $due_now = false; $is_paid = $item->is_paid(); if (!$is_paid) { $diff = MS_Helper_Period::subtract_dates($item->due_date, MS_Helper_Period::current_date(), null, true); $due_now = $diff < 0; } $due_date = MS_Helper_Period::format_date($item->due_date); if ($due_now) { $html = sprintf('<span class="due-now" title="%2$s">%1$s</span>', $due_date, __('Payment is overdue', 'membership2')); } elseif ($item->pay_date) { $pay_date = MS_Helper_Period::format_date($item->pay_date, 'M j, Y'); $html = sprintf('<span class="is-paid" title="%2$s">%1$s</span>', $due_date, sprintf(__('Paid: %s', 'membership2'), $pay_date)); } else { $html = sprintf('<span>%1$s</span>', $due_date); } return $html; }
/** * 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 column_due_date($item, $column_name) { $due_now = false; if (!$item->is_paid()) { $diff = MS_Helper_Period::subtract_dates($item->due_date, MS_Helper_Period::current_date(), null, true); $due_now = $diff < 0; } $date = MS_Helper_Period::format_date($item->due_date); if ($due_now) { $html = sprintf('<span class="due-now" title="%2$s">%1$s</span>', $date, __('Payment is overdue', MS_TEXT_DOMAIN)); } else { $html = sprintf('<span>%1$s</span>', $date); } return $html; }
/** * 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); }
/** * Prepare the PayPal IPN fields * * Details here: * https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/ * * @since 1.0.0 * @return array */ private function prepare_fields() { $subscription = $this->data['ms_relationship']; $membership = $subscription->get_membership(); if (0 === $membership->price) { return; } $gateway = $this->data['gateway']; $invoice = $subscription->get_current_invoice(); $regular_invoice = null; $settings = MS_Factory::load('MS_Model_Settings'); $nonce = wp_create_nonce($gateway->id . '_' . $subscription->id); $cancel_url = MS_Model_Pages::get_page_url(MS_Model_Pages::MS_PAGE_REGISTER); $return_url = esc_url_raw(add_query_arg(array('ms_relationship_id' => $subscription->id), MS_Model_Pages::get_page_url(MS_Model_Pages::MS_PAGE_REG_COMPLETE, false))); $fields = array('_wpnonce' => array('id' => '_wpnonce', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $nonce), 'charset' => array('id' => 'charset', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => 'utf-8'), 'business' => array('id' => 'business', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $gateway->merchant_id), 'cmd' => array('id' => 'cmd', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => '_xclick-subscriptions'), 'bn' => array('id' => 'bn', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => 'incsub_SP'), 'item_name' => array('id' => 'item_name', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $membership->name), 'item_number' => array('id' => 'item_number', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $membership->id), 'currency_code' => array('id' => 'currency_code', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => MS_Plugin::instance()->settings->currency), 'return' => array('id' => 'return', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $return_url), 'cancel_return' => array('id' => 'cancel_return', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $cancel_url), 'notify_url' => array('id' => 'notify_url', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $gateway->get_return_url()), 'country' => array('id' => 'country', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $gateway->paypal_site), 'no_note' => array('id' => 'no_note', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => 1), 'no_shipping' => array('id' => 'no_shipping', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => 1), 'invoice' => array('id' => 'invoice', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $invoice->id)); $fields['submit'] = array('id' => 'submit', 'type' => MS_Helper_Html::INPUT_TYPE_IMAGE, 'value' => 'https://www.paypal.com/en_US/i/btn/btn_subscribe_LG.gif', 'alt' => __('PayPal - The safer, easier way to pay online', 'membership2')); // custom pay button defined in gateway settings $custom_label = $gateway->pay_button_url; if (!empty($custom_label)) { if (false !== strpos($custom_label, '://')) { $fields['submit']['value'] = $custom_label; } else { $fields['submit'] = array('id' => 'submit', 'type' => MS_Helper_Html::INPUT_TYPE_SUBMIT, 'value' => $custom_label); } } // Trial period if ($subscription->is_trial_eligible()) { $fields['a1'] = array('id' => 'a1', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $invoice->trial_price); $trial_type = MS_Helper_Period::get_period_value($membership->trial_period, 'period_type'); $trial_type = strtoupper($trial_type[0]); $trial_value = MS_Helper_Period::get_period_value($membership->trial_period, 'period_unit'); $trial_value = MS_Helper_Period::validate_range($trial_value, $trial_type); $fields['p1'] = array('id' => 'p1', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $trial_value); $fields['t1'] = array('id' => 't1', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $trial_type); } // Membership price $membership_price = $invoice->total; $membership_price = MS_Helper_Billing::format_price($membership_price); $fields['a3'] = array('id' => 'a3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $membership_price); $fields['amount'] = array('id' => 'amount', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $membership_price); $recurring = 0; switch ($membership->payment_type) { // == RECURRING PAYMENTS case MS_Model_Membership::PAYMENT_TYPE_RECURRING: $period_type = MS_Helper_Period::get_period_value($membership->pay_cycle_period, 'period_type'); $period_type = strtoupper($period_type[0]); $period_value = MS_Helper_Period::get_period_value($membership->pay_cycle_period, 'period_unit'); $period_value = MS_Helper_Period::validate_range($period_value, $period_type); $fields['p3'] = array('id' => 'p3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $period_value); $fields['t3'] = array('id' => 't3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $period_type); // This makes the payments recurring! $recurring = 1; break; // == FINITE END DATE // == FINITE END DATE case MS_Model_Membership::PAYMENT_TYPE_FINITE: $period_type = MS_Helper_Period::get_period_value($membership->period, 'period_type'); $period_type = strtoupper($period_type[0]); $period_value = MS_Helper_Period::get_period_value($membership->period, 'period_unit'); $period_value = MS_Helper_Period::validate_range($period_value, $period_type); $fields['p3'] = array('id' => 'p3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $period_value); $fields['t3'] = array('id' => 't3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $period_type); break; // == DATE RANGE // == DATE RANGE case MS_Model_Membership::PAYMENT_TYPE_DATE_RANGE: $period_value = MS_Helper_Period::subtract_dates($membership->period_date_end, $membership->period_date_start); $period_value = MS_Helper_Period::validate_range($period_value, 'D'); $fields['p3'] = array('id' => 'p3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $period_value); $fields['t3'] = array('id' => 't3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => 'D'); break; // == PERMANENT // == PERMANENT case MS_Model_Membership::PAYMENT_TYPE_PERMANENT: /* * Permanent membership: Set the subscription range to 5 years! * PayPal requires us to provide the subscription range and the * maximum possible value is 5 years. */ $period_value = MS_Helper_Period::validate_range(5, 'Y'); $fields['p3'] = array('id' => 'p3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $period_value); $fields['t3'] = array('id' => 't3', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => 'Y'); break; } if (1 == $recurring) { if (1 == $membership->pay_cycle_repetitions) { $recurring = 0; } elseif ($membership->pay_cycle_repetitions > 1) { /** * Recurring times. * The number of times that a recurring payment is made. */ $fields['srt'] = array('id' => 'srt', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $membership->pay_cycle_repetitions); } } /** * Recurring field. * 0 - one time payment * 1 - recurring payments */ $fields['src'] = array('id' => 'src', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $recurring); /** * Modify current subscription field. * value != 0 does not allow trial period. * 0 - allows subscribers only to sign up for new subscriptions * 1 - allows subscribers to sign up for new subscriptions and modify their current subscriptions * 2 - allows subscribers to modify only their current subscriptions */ $modify = !empty($move_from_id); $fields['modify'] = array('id' => 'modify', 'type' => MS_Helper_Html::INPUT_TYPE_HIDDEN, 'value' => $modify ? 2 : 0); if ($gateway->is_live_mode()) { $this->data['action_url'] = 'https://www.paypal.com/cgi-bin/webscr'; } else { $this->data['action_url'] = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; } return apply_filters('ms_gateway_paypalstandard_view_prepare_fields', $fields); }
/** * Add a message in the "send queue". * * @since 1.0.0 * @api * @param int $subscription_id The membership relationship ID to add to queue. */ public function add_to_queue($subscription_id) { do_action('ms_model_communication_add_to_queue_before', $this); /** * Documented in process_queue() * * @since 1.0.0 */ if (MS_Plugin::get_modifier('MS_STOP_EMAILS')) { $subscription = MS_Factory::load('MS_Model_Relationship', $subscription_id); do_action('lib2_debug_log', sprintf('Following Email was not sent: "%s" to user "%s".', $this->type, $subscription->user_id)); return false; } $is_enqueued = array_key_exists($subscription_id, $this->queue); if ($this->enabled && !$is_enqueued) { $can_add = true; /** * Check if email enqueuing is limited to prevent duplicate emails. * * Use setting `define( 'MS_DUPLICATE_EMAIL_HOURS', 24 )` to prevent * duplicate emails from being sent for 24 hours. * * @var int Number of hours */ $pause_hours = 0; if (defined('MS_DUPLICATE_EMAIL_HOURS') && is_numeric(MS_DUPLICATE_EMAIL_HOURS)) { $pause_hours = MS_DUPLICATE_EMAIL_HOURS; } if ($pause_hours > 0) { if (array_key_exists($subscription_id, $this->sent_queue)) { $pause_hours = apply_filters('ms_model_communication_hours_before_resend', $pause_hours); /* * The sent_queue is saved in DB and only contains messages * from the current Communications object. So * $subscription_id defines the email contents and receiver. */ $sent_date = $this->sent_queue[$subscription_id]; $now = MS_Helper_Period::current_time(); $current_delay = MS_Helper_Period::subtract_dates($now, $sent_date, HOURS_IN_SECONDS); $can_add = $current_delay >= $pause_hours; } } if ($can_add) { $this->queue[$subscription_id] = MS_Helper_Period::current_time(); } } do_action('ms_model_communication_add_to_queue_after', $this); }
/** * Calculate pro rate value. * * Pro rate using remaining membership days. * * @since 1.0.1.0 * * @return float The pro rate value. */ protected function get_discount($subscription) { $value = 0; $membership = $subscription->get_membership(); if (MS_Model_Membership::PAYMENT_TYPE_PERMANENT !== $membership->payment_type) { $invoice = $subscription->get_previous_invoice(); if ($invoice && $invoice->is_paid()) { switch ($subscription->status) { case MS_Model_Relationship::STATUS_TRIAL: // No Pro-Rate given for trial memberships. break; case MS_Model_Relationship::STATUS_ACTIVE: case MS_Model_Relationship::STATUS_WAITING: case MS_Model_Relationship::STATUS_CANCELED: $remaining_days = $subscription->get_remaining_period(); $total_days = MS_Helper_Period::subtract_dates($subscription->expire_date, $subscription->start_date); $value = $remaining_days / $total_days; $value *= $invoice->total; break; default: // No Pro-Rate for other subscription status. break; } } } return apply_filters('ms_addon_prorate_get_discount', $value, $subscription); }