Skip to content

Instantly share code, notes, and snippets.

@wpmudev-sls
Last active July 16, 2019 09:10
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/8ee83a8917d9369ddf142e0d26c68dc4 to your computer and use it in GitHub Desktop.
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
<?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 );
}
}
@wpmudev-sls
Copy link
Author

wpmudev-sls commented Jun 2, 2019

Since new Plan id is created it would need to add the following

		if ( 'plan' == $type ) {
			$plan_id = get_post_meta( $id, '_ms_stripeplan_plan_id', true );
			if ( ! empty( $plan_id ) ) {
				return $plan_id;
			}
		}

in MS_Gateway_Stripeplan::get_the_id()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment