Last active
July 16, 2019 09:10
-
-
Save wpmudev-sls/8ee83a8917d9369ddf142e0d26c68dc4 to your computer and use it in GitHub Desktop.
[Membership 2] - Update Plans on Stripe Products. Instead of deleting/adding plan using same Plan id and creating a new Product, we create a new Plan in existing Product and switch all Stripe subscriptions to that one
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Plugin Name: [Membership 2] - Update Plans on Stripe Products | |
* Plugin URI: https://premium.wpmudev.org/ | |
* Description: Instead of deleting/adding plan using same Plan id and creating a new Product, we create a new Plan in existing Product and switch all Stripe subscriptions to that one | |
* Author: Panos Lyrakis @ WPMUDEV | |
* Author URI: https://premium.wpmudev.org/ | |
* License: GPLv2 or later | |
*/ | |
if ( ! defined( 'ABSPATH' ) ) { | |
exit; | |
} | |
if ( ! class_exists( 'WPMUDEV_MS_Update_Stripe_Plan' ) ) { | |
class WPMUDEV_MS_Update_Stripe_Plan { | |
const STRIPEPLAN_ID = 'stripeplan'; | |
private static $_instance = null; | |
protected $test_secret_key = false; | |
protected $secret_key = false; | |
protected $test_publishable_key = false; | |
protected $publishable_key = false; | |
protected $mode = null; | |
protected $stripe_plan_options = null; | |
protected static $prorate_off = true; | |
protected $_api; | |
public static function get_instance() { | |
if( is_null( self::$_instance ) ){ | |
self::$_instance = new WPMUDEV_MS_Update_Stripe_Plan(); | |
} | |
return self::$_instance; | |
} | |
private function __construct() { | |
if ( ! class_exists( 'MS_Gateway_Stripeplan' ) ) { | |
return; | |
} | |
$this->init(); | |
add_action( 'ms_saved_MS_Model_Membership', array( $this, 'override_plan_updater' ), 1 ); | |
} | |
public function override_plan_updater( $membership ) { | |
if ( | |
$membership->is_free() || | |
$membership->payment_type != MS_Model_Membership::PAYMENT_TYPE_RECURRING | |
) { | |
return; | |
} | |
$plan_id = MS_Gateway_Stripeplan::get_the_id( $membership->id, 'plan' ); | |
$new_plan_id = ( strpos( $plan_id, '-mspcode' ) !== false ) ? | |
substr( $plan_id, 0, strrpos( $plan_id, '-mspcode' ) ) . '-mspcode' . current_time( 'timestamp', 1 ) : | |
$plan_id . '-mspcode' . current_time( 'timestamp', 1 ); | |
$plan = $this->fetch_stripe_plan( $plan_id ); | |
$product_id = isset( $plan->product ) ? $plan->product : null; | |
if ( is_null( $product_id ) ) { | |
// This means that there is no Product for this Membership on Stripe yet. | |
// We do not need to override default M2 behavior in this case | |
return; | |
} | |
// From this part on we are overriding the default M2 behavior | |
$this->remove_default_action(); | |
if ( absint( $membership->price * 100 ) == $plan->amount ) { | |
// No need to update the Stripe plan if there is no price change. | |
// Lets not move all subscribers to new plan without a reason :) | |
return; | |
} | |
$plan_data = array( | |
'id' => $new_plan_id, | |
'amount' => 0, | |
); | |
// 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['product'] = array( | |
// "name" => $membership->name | |
//); | |
// We need to use the existing product | |
$plan_data['product'] = $product_id; | |
$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->create_new_plan( $plan_data, $product_id ); | |
update_post_meta( $membership->id, '_ms_stripeplan_plan_id', $new_plan_id ); | |
$this->sync_subscriptions_to_plan( $membership->id, $new_plan_id ); | |
} | |
} | |
public function create_new_plan( $plan_data, $product_id ) { | |
$secret_key = $this->get_secret_key(); | |
$url = "https://api.stripe.com/v1/plans"; | |
$auth = base64_encode( $secret_key . ':' . '' ); | |
$args = [ | |
'headers' => [ | |
'Authorization' => "Basic $auth" | |
], | |
'body' => $plan_data | |
]; | |
$response = wp_remote_post( $url, $args ); | |
if ( is_wp_error( $response ) ) { | |
return false; | |
} | |
$response_body = wp_remote_retrieve_body( $response ); | |
return json_decode( $response_body ); | |
} | |
public function sync_subscriptions_to_plan( $membership_id = null, $new_plan_id = null ) { | |
if ( is_null( $membership_id ) ) { | |
return false; | |
} | |
if ( $membership_id instanceof MS_Model_Membership ) { | |
$membership_id = $membership_id->id; | |
} | |
if ( is_null( $new_plan_id ) ) { | |
$new_plan_id = get_post_meta( $membership_id, '_ms_stripeplan_plan_id', true ); | |
if ( empty( $new_plan_id ) ) { | |
return; | |
} | |
} | |
$subscriptions = MS_Model_Relationship::get_subscriptions( | |
array( 'membership_id' => $membership_id ) | |
); | |
foreach ( $subscriptions as $key => $subscription ) { | |
if ( self::STRIPEPLAN_ID != $subscription->gateway_id || empty( $subscription->payments ) ) { | |
continue; | |
} | |
$stripe_subscription_id = $this->get_stripe_subscription_id( $subscription->payments ); | |
if ( ! ! $stripe_subscription_id ) { | |
$this->sync_subscription_to_plan( $stripe_subscription_id, $new_plan_id ); | |
} | |
} | |
} | |
private function get_stripe_subscription_id( $payments ) { | |
foreach ( array_reverse( $payments ) as $key => $payment ) { | |
if ( isset( $payment[ 'gateway' ] ) && self::STRIPEPLAN_ID == $payment[ 'gateway' ] ) { | |
if ( isset( $payment['external_id'] ) ) { | |
return $payment['external_id']; | |
} | |
} | |
} | |
return false; | |
} | |
public function sync_subscription_to_plan( $stripe_subscription_id = null, $new_plan_id = null ) { | |
if ( is_null( $stripe_subscription_id ) || is_null( $stripe_subscription_id ) ) { | |
return false; | |
} | |
$secret_key = $this->get_secret_key(); | |
$url = "https://api.stripe.com/v1/subscriptions/{$stripe_subscription_id}"; | |
$subscription_list_id = $this->get_subscription_list_id( $stripe_subscription_id ); | |
if ( ! $subscription_list_id ) { | |
error_log('STRIPE SYNC ERROR : No subscription list id found'); | |
return; | |
} | |
$auth = base64_encode( $secret_key . ':' . '' ); | |
$body = array( | |
'cancel_at_period_end' => 'false', | |
'items[0][id]' => $subscription_list_id, | |
'items[0][plan]' => $new_plan_id, | |
//'prorate' => 'false' | |
); | |
if ( self::$prorate_off ) { | |
$body[ 'prorate' ] = 'false'; | |
} | |
$args = [ | |
'headers' => [ | |
'Authorization' => "Basic $auth" | |
], | |
'body' => $body | |
]; | |
$response = wp_remote_post( $url, $args ); | |
if ( is_wp_error( $response ) ) { | |
return false; | |
} | |
$response_body = wp_remote_retrieve_body( $response ); | |
return json_decode( $response_body ); | |
} | |
public function get_stripe_subscription( $stripe_subscription_id ) { | |
$secret_key = $this->get_secret_key(); | |
$auth = base64_encode( $secret_key . ':' . '' ); | |
$url = "https://api.stripe.com/v1/subscriptions/{$stripe_subscription_id}"; | |
$args = [ | |
'headers' => [ | |
'Authorization' => "Basic $auth" | |
] | |
]; | |
$response = wp_remote_get( $url, $args ); | |
if ( is_wp_error( $response ) ) { | |
return false; | |
} | |
$response_body = wp_remote_retrieve_body( $response ); | |
return json_decode( $response_body ); | |
} | |
private function get_subscription_list_id( $stripe_subscription_id ) { | |
$stripe_subscription = $this->get_stripe_subscription( $stripe_subscription_id ); | |
return isset( $stripe_subscription->items->data[0]->id ) ? $stripe_subscription->items->data[0]->id : false; | |
} | |
private function fetch_stripe_plan( $plan_id ) { | |
$url = "https://api.stripe.com/v1/plans/{$plan_id}"; | |
$secret_key = $this->get_secret_key(); | |
$auth = base64_encode( $secret_key . ':' . '' ); | |
$args = [ | |
'headers' => [ | |
'Authorization' => "Basic $auth" | |
] | |
]; | |
$response = wp_remote_get( $url, $args ); | |
if ( is_wp_error( $response ) ) { | |
return false; | |
} | |
$response_body = wp_remote_retrieve_body( $response ); | |
return json_decode( $response_body ); | |
} | |
private function fetch_stripe_products() { | |
$secret_key = $this->get_secret_key(); | |
$ch = curl_init(); | |
curl_setopt( $ch, CURLOPT_URL, 'https://api.stripe.com/v1/products' ); | |
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); | |
curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, 'GET' ); | |
curl_setopt( $ch, CURLOPT_USERPWD, $secret_key . ':' . '' ); | |
$result = curl_exec( $ch ); | |
if ( curl_errno( $ch ) ) { | |
print_r( 'Error:' . curl_error( $ch ) ); | |
} | |
curl_close ( $ch ); | |
return json_decode( $result )->data; | |
} | |
private function get_publishable_key() { | |
return ( 'sandbox' == $this->mode ) ? $this->test_publishable_key : $this->publishable_key; | |
} | |
private function get_secret_key() { | |
return ( 'sandbox' == $this->mode ) ? $this->test_secret_key : $this->secret_key; | |
} | |
private function init() { | |
if ( is_null( $this->mode ) ) { | |
$stripe_plan_options = $this->get_stripeplan_options(); | |
if ( '' == $stripe_plan_options['mode'] || 'sandbox' == $stripe_plan_options['mode'] ) { | |
$this->mode = 'sandbox'; | |
} else { | |
$this->mode = 'live'; | |
} | |
$this->test_publishable_key = $stripe_plan_options['test_publishable_key']; | |
$this->publishable_key = $stripe_plan_options['publishable_key']; | |
$this->test_secret_key = $stripe_plan_options['test_secret_key']; | |
$this->secret_key = $stripe_plan_options['secret_key']; | |
} | |
} | |
private function get_stripeplan_options() { | |
if ( is_null( $this->stripe_plan_options ) ) { | |
$this->stripe_plan_options = get_option( 'ms_gateway_stripeplan' ); | |
} | |
return $this->stripe_plan_options; | |
} | |
private function remove_default_action() { | |
global $wp_filter; | |
$tag = 'ms_saved_MS_Model_Membership'; | |
$callback_method = 'update_stripe_data_membership'; | |
$callback_class = 'MS_Gateway_Stripeplan'; | |
foreach ( $wp_filter[$tag]->callbacks as $key => $callback_array ) { | |
foreach ( $callback_array as $c_key => $callback ) { | |
if ( substr_compare( $c_key, $callback_method, strlen( $c_key ) - strlen( $callback_method ), strlen( $callback_method ) ) === 0 ) { | |
if ( $callback['function'][0] instanceof $callback_class ) { | |
unset( $wp_filter[$tag]->callbacks[$key][$c_key] ); | |
} | |
} | |
} | |
} | |
} | |
} | |
if ( ! function_exists( 'wpmudev_ms_update_stripe_plan' ) ) { | |
function wpmudev_ms_update_stripe_plan(){ | |
return WPMUDEV_MS_Update_Stripe_Plan::get_instance(); | |
}; | |
add_action( 'plugins_loaded', 'wpmudev_ms_update_stripe_plan', 10 ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Since new Plan id is created it would need to add the following
in
MS_Gateway_Stripeplan::get_the_id()