Skip to content

Instantly share code, notes, and snippets.

@wpmudev-sls
Created November 6, 2019 10:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wpmudev-sls/10c04c432b278ad02c37f22cff3bec78 to your computer and use it in GitHub Desktop.
Save wpmudev-sls/10c04c432b278ad02c37f22cff3bec78 to your computer and use it in GitHub Desktop.
[Membership] - Deactivated Subscriptions
<?php
/**
* Plugin Name: [Membership] - Deactivated Subscriptions
* Plugin URI: https://premium.wpmudev.org/
* Description: Provides an admin page to list and manage Deactivated Subscriptions
* Author: Panos Lyrakis @ WPMUDEV
* Author URI: https://premium.wpmudev.org/
* License: GPLv2 or later
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WPMUDEV_MS_Manage_Subscriptions' ) ) {
class WPMUDEV_MS_Manage_Subscriptions {
private static $_instance = null;
public static function get_instance() {
if( is_null( self::$_instance ) ){
self::$_instance = new WPMUDEV_MS_Manage_Subscriptions();
}
return self::$_instance;
}
private function __construct() {
add_action( 'admin_menu', array( $this, 'admin_menu' ), 999 );
add_action( 'wp_ajax_wpmudev_ms_fix_subscription', array( $this, 'fix_subscription' ) );
}
public function fix_subscription() {
$subscription_id = filter_input( INPUT_POST, 'subscription_id', FILTER_VALIDATE_INT );
$issue_type = filter_input( INPUT_POST, 'issue_type', FILTER_DEFAULT );
$subscription = MS_Factory::load( 'MS_Model_Relationship', $subscription_id );
$calculated_expire_date = $this->get_subscription_calculated_expire_date( $subscription );
if ( strtotime( $calculated_expire_date ) >= strtotime( MS_Helper_Period::current_date() ) ) {
update_post_meta( $subscription_id, 'expire_date', $calculated_expire_date );
// Maybe update the last invoice date too?
$invoice = $subscription->get_current_invoice();
if ( $invoice->is_paid() ) {
update_post_meta( $invoice->id, 'due_date', $calculated_expire_date );
}
//Should be set to active
$subscription_suggested_status = MS_Model_Relationship::STATUS_ACTIVE;
if ( $subscription->status != $subscription_suggested_status ) {
$this->set_status( $subscription, MS_Model_Relationship::STATUS_ACTIVE );
}
$return = array(
'success' => true,
'subscription_id' => $subscription_id,
'bg_color' => 'rgba(85, 239, 196, 0.8)'
);
wp_send_json($return);
}
}
public function admin_menu() {
add_submenu_page(
'membership2',
'Manage Subscriptions',
'Manage Subscriptions',
'manage_options',
'ms-manage-subscriptions-list',
array( $this, 'admin_page_display' )
);
}
protected function list_subscriptions( $echo = true ) {
$args = array( 'numberposts' => -1 );
//$allowed_statuses = array( 'pending', 'waiting', 'active', 'trial', 'canceled', 'trial_expired', 'expired', 'deactivated' );
$allowed_statuses = array( 'pending', 'expired', 'deactivated' );
$args['meta_key'] = 'status';
$args['meta_value'] = $allowed_statuses;
if ( isset( $_REQUEST['status'] ) && in_array( $_REQUEST['status'], $allowed_statuses ) ) {
//$args['meta_key'] = 'status';
$args['meta_value'] = $_REQUEST['status'];
}
$subscriptions = $this->get_subscriptions( $args );
$out = "";
if ( empty( $subscriptions ) ) {
return "<h3>No subscriptions found in Database</h3>";
}
$out .= "<table class=\"wp-list-table widefat fixed striped subscriptions\">";
$out .= "<thead>";
$out .= "<tr>";
$out .= "<th>#</th>";
$out .= "<th style=\"width:8%;\">ID</th>";
$out .= "<th style=\"width:8%;\">User ID</th>";
$out .= "<th style=\"width:8%;\">Membership ID</th>";
$out .= "<th style=\"width:8%;\">Status</th>";
$out .= "<th style=\"width:8%;\">Gateway</th>";
$out .= "<th style=\"width:8%;\">Payment type</th>";
$out .= "<th style=\"width:8%;\">Start</th>";
$out .= "<th style=\"width:8%;\">Expire</th>";
$out .= "<th style=\"width:8%;\">Calculated Expire</th>";
$out .= "<th style=\"width:8%;\">Created</th>";
$out .= "<th style=\"width:16%;\">To Fix</th>";
$out .= "</tr>";
$out .= "</thead>";
$count = 1;
//$ids_array = array();
foreach ( $subscriptions as $subscription ) {
//$ids_array[] = $subscription->ID;
$user = get_user_by( 'ID', $subscription->post_author );
$subscriptions_link = admin_url( "admin.php?page=membership2-add-member&user_id={$user->ID}" );
$tofix_msg = '';
$tofix_type = '';
$bg_color = "rgba(85, 239, 196, 0.8)";
if ( isset( $subscription->to_fix) && isset( $subscription->to_fix[ 'type' ] ) && isset( $subscription->to_fix[ 'message' ] ) ) {
$tofix_msg = $subscription->to_fix[ 'message' ];
$tofix_type = $subscription->to_fix[ 'type' ];
$bg_color = "rgba(255, 118, 117, 0.6)";
}
$out .= "<tr style=\"background-color: {$bg_color};\">";
$out .= "<td class=\"subscription-count\">{$count}</td>";
$out .= "<td class=\"subscription-id\">{$subscription->ID}</td>";
$out .= "<td class=\"subscription-author\"><a href=\"{$subscriptions_link}\">{$subscription->post_author}<br / >{$user->user_login}</a></td>";
$out .= "<td class=\"subscription-membership\">{$subscription->meta->membership_id}</td>";
$out .= "<td class=\"subscription-status\">{$subscription->meta->status}</td>";
$out .= "<td class=\"subscription-gateway\">{$subscription->meta->gateway_id}</td>";
$out .= "<td class=\"subscription-payment_type\">{$subscription->meta->payment_type}</td>";
$out .= "<td class=\"subscription-start\">{$subscription->meta->start_date}</td>";
$out .= "<td class=\"subscription-expire\">{$subscription->meta->expire_date}</td>";
$out .= "<td class=\"subscription-calculated-expire\">{$subscription->meta->calculated_expire_date}</td>";
$out .= "<td class=\"subscription-date\">{$subscription->post_date}</td>";
$out .= "<td class=\"subscription-to-fix\">{$tofix_msg}</td>";
$out .= "</tr>";
$count ++;
}
$out .= "</table>";
if ( ! $echo ) {
return $out;
}
print( $out );
}
public function get_subscriptions ( $args = array(), $meta_keys = array() ) {
$defaults = array(
//author
'numberposts' => -1,
//'category' => 0,
'orderby' => 'ID',
'order' => 'DESC',
'include' => array(),
'exclude' => array(),
'meta_key' => '',
'meta_value' => '',
'post_type' => 'ms_relationship',
'post_status' => 'any',
'suppress_filters' => true,
);
$args = wp_parse_args( $args, $defaults );
$meta = array();
$subscriptions = get_posts( $args );
if ( ! empty( $subscriptions ) ) {
foreach ( $subscriptions as $s_key => $subscription ) {
$subscription = ( array ) $subscription;
if ( ! is_array( $meta_keys ) || empty( $meta_keys ) ) {
$_meta = get_post_meta( $subscription['ID'] );
foreach( $_meta as $m_key => $m_array ) {
$meta[ $m_key ] = $m_array[0];
}
} else {
foreach ( $meta_keys as $m_key => $meta_key ) {
$meta[$meta_key] = get_post_meta( $subscription['ID'], $meta_key, true );
}
}
$ms_subscription = MS_Factory::load( 'MS_Model_Relationship', intval( $subscription[ 'ID' ] ) );
$meta[ 'calculated_expire_date' ] = $this->get_subscription_calculated_expire_date( $ms_subscription );
$subscription[ 'to_fix' ] = $this->subscription_errors( $ms_subscription, $meta );
$subscription['meta'] = ( object ) $meta;
$subscriptions[ $s_key ] = ( object ) $subscription;
}
}
return $subscriptions;
}
public function subscription_errors( $subscription, $meta ) {
if ( is_array( $subscription ) ) {
$subscription = MS_Factory::load( 'MS_Model_Relationship', intval( $subscription[ 'ID' ] ) );
}
if ( ! $subscription instanceof MS_Model_Relationship ) {
return false;
}
$response = array();
$calculated_expire_date = isset( $meta[ 'calculated_expire_date' ] ) ? $meta[ 'calculated_expire_date' ] : $subscription->expire_date;
if ( strtotime( $calculated_expire_date ) >= strtotime( MS_Helper_Period::current_date() ) ) {
// Should be set to active
// TODO : Add a method to calculate suggested method per subscription
$subscription_suggested_status = MS_Model_Relationship::STATUS_ACTIVE;
if ( $subscription->status != $subscription_suggested_status ) {
$issue_code = 'status';
$response[ 'type' ] = $issue_code;
$response[ 'message' ] = "Subscription status is set to <strong>
{$subscription->status}</strong> but it seems it should be <strong>{$subscription_suggested_status}</strong> instead<div><button class=\"button button-primary ms-fix-subscription\" data-issue-type=\"{$issue_code}\" data-subscription=\"{$subscription->id}\" id=\"ms-fix-subscription-{$subscription->id}\">Fix this</button></div>";
}
}
return $response;
}
public function get_subscription_calculated_expire_date( $subscription ) {
if ( is_array( $subscription ) ) {
$subscription = MS_Factory::load( 'MS_Model_Relationship', intval( $subscription[ 'ID' ] ) );
}
if ( ! $subscription instanceof MS_Model_Relationship ) {
return false;
}
if (
empty( $subscription->expire_date ) ||
( $subscription->is_trial_eligible() && strtotime( $subscription->trial_expire_date ) >= strtotime( MS_Helper_Period::current_date() ) )
)
{
return;
}
//$last_payment_date = $subscription->expire_date;
//if ( 'admin' != $subscription->gateway_id && 'free' != $subscription->gateway_id ) {
$payment = $this->get_subscription_last_payment( $subscription );
$subscription_expiration_date = $subscription->expire_date;
if ( ! ! $payment && ! empty( $payment ) ) {
$last_payment_date = $payment->date;
$calculated_expire_date = $this->get_subscription_expiration_date( $subscription, $last_payment_date );
if ( strtotime( $subscription->expire_date ) > strtotime( $calculated_expire_date ) ) {
$calculated_expire_date = $subscription->expire_date;
}
return $calculated_expire_date;
}
return false;
}
private function log( $message ) {
if ( ! defined( "WP_DEBUG_LOG" ) || ! WP_DEBUG_LOG ) {
return;
}
$time = date( 'd-M-Y H:i:s' );
error_log(
"[ {$time} ] \n{$message}\n\n\n",
3,
WP_CONTENT_DIR . "/ms-subs-debug-" . date( 'd-m-Y' ) . ".log"
);
}
private function set_status( $subscription, $status ) {
update_post_meta( $subscription->id, 'status', $status );
if ( MS_Model_Relationship::STATUS_EXPIRED == $status ) {
// Since this subscription is expired, lets check if we need to move membership:
$membership = $subscription->get_membership();
if ( ! empty( $membership->on_end_membership_id ) ) {
$this->maybe_move_membership( $subscription, $membership );
}
}
}
private function maybe_move_membership( $subscription, $membership ) {
// Do not continue if on_end_membership_id is empty.
if ( empty( $membership->on_end_membership_id ) ) {
return;
}
// Deactivate the current membership.
$subscription->deactivate_membership();
// Move membership to configured 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', $subscription->user_id );
$new_subscription = $member->add_membership(
$membership->on_end_membership_id,
$subscription->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 its free we set it as active
*/
if ( ! $new_membership->is_free() ) {
$new_subscription->status = MS_Model_Relationship::STATUS_PENDING;
} else {
$new_subscription->status = MS_Model_Relationship::STATUS_ACTIVE;
}
// Save new membership.
$new_subscription->save();
}
}
public function get_subscription_last_payment( $subscription ) {
$transaction = $this->get_subscription_last_transaction( $subscription->id );
if ( ! $transaction || empty( $transaction ) ) {
return false;
}
return $this->payment_from_transaction( $transaction );
}
public function get_subscription_last_transaction( $subscription_id ) {
if ( is_object( $subscription_id ) ) {
if ( ! isset( $subscription_id->id ) ) {
return false;
}
$subscription_id = $subscription_id->id;
}
$transaction = array();
$args = array(
'numberposts' => 1,
'offset' => 0,
'post_status' => 'any', //'private', // transaction logs have private status in posts table
'post_type' => 'ms_transaction_log',
'meta_query' => array(
array(
'key' => 'subscription_id',
'value' => $subscription_id,
'compare' => '=',
),
array(
'key' => 'success',
'value' => 'ok',
'compare' => '='
)
)
);
$transactions = get_posts( $args );
if ( ! empty( $transactions ) ) {
return $transactions[0];
}
else {
return false;
}
}
// This returns the required fields of the transaction.
public function payment_from_transaction( $transaction ) {
$payment = array();
if ( ! $transaction instanceof WP_Post || 'ms_transaction_log' != $transaction->post_type ) {
return $payment;
}
$payment = array(
'id' => $transaction->ID,
'date' => $transaction->post_date
);
/*
// Currently we don't need to use transaction meta
$meta = get_post_meta( $transaction->ID );
foreach ( $meta as $meta_key => $meta_value ) {
if ( isset( $meta_value[0] ) && ! isset( $payment[ $meta_key ] ) ) {
$payment[ $meta_key ] = $meta_value[0];
}
}
*/
return (object) $payment;
}
public function get_subscription_expiration_date( $subscription, $last_payment_date ) {
$grace_period = apply_filters(
'ms_subscription_expiration_grace_period',
1,
$subscription
);
$period_duration = $this->get_subscription_duration( $subscription );
//$this->log( '$period_duration: ' . print_r( $period_duration,true ) );
$expiration_date = date( 'Y-m-d H:i:s', strtotime( $last_payment_date . "+{$period_duration->period_unit} {$period_duration->period_type}" ) );
//$this->log( '$expiration_date: ' . $expiration_date );
//return date( 'Y-m-d', strtotime( $expiration_date . "+{$grace_period} days" ) );
// We must not add the grace period here. The grace period is added on top of the expiration date of the subscription
return date( 'Y-m-d', strtotime( $expiration_date ) );
}
public function get_subscription_duration( $subscription ) {
if ( ! $subscription instanceof MS_Model_Relationship ) {
return false;
}
return (object) get_post_meta( $subscription->membership_id, 'period', true );
return (object) MS_Factory::load(
'MS_Model_Membership',
$subscription->membership_id
)->pay_cycle_period;
}
public function admin_page_display() {
?>
<div class="ms-wrap wrap">
<h2 class="ms-settings-title">Subscriptons list from DB</h2>
<div style="padding: 20px; background: #fff; color: #555; font-size: 16px;">
<?php $this->list_subscriptions(); ?>
</div>
</div>
<script type="text/javascript">
($=>{
const ms_fix_subscription = {
run : function( subscription_id, issue_type, button ) {
var spinner = $( '<img/>', {
src: '/wp-admin/images/wpspin_light.gif',
'class': 'ms-sfix-spinner-image'
}).css( { 'width':'14px', 'margin-left':'5px' } ).appendTo( button ),
data = {
action: 'wpmudev_ms_fix_subscription',
security: '<?php echo wp_create_nonce( "wpmudev_ms_fix_subscription" ); ?>',
subscription_id: subscription_id,
issue_type: issue_type
};
$.post( ajaxurl, data, function(response) {
$( '.ms-sfix-spinner-image' ).hide( 200, function(){ $(this).remove(); } );
if( response.success ){
let button = $( `#ms-fix-subscription-${response.subscription_id}` ),
row = button.closest( 'tr' );
button.parent().html( 'Fixed!' );
row.css( 'background-color', response.bg_color ) ;
}
$( '.ms-fix-subscription' ).prop('disabled', false);
});
}
}
$( document ).ready(function(){
$( '.ms-fix-subscription' ).on( 'click', function(){
$( '.ms-fix-subscription' ).prop('disabled', true);
ms_fix_subscription.run(
$(this).data( 'subscription' ),
$(this).data( 'issue_type' ),
$(this)
);
} );
});
})(jQuery);
</script>
<?php
}
}
if ( ! function_exists( 'wpmudev_ms_manage_subscriptions' ) ) {
function wpmudev_ms_manage_subscriptions(){
return WPMUDEV_MS_Manage_Subscriptions::get_instance();
};
add_action( 'plugins_loaded', 'wpmudev_ms_manage_subscriptions', 10 );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment