Stripe price is not update properly in Membership 2 Pro

Existing customers are still getting charged $49.99/mo rather than the new price $54.99. We need to update existing subscribers on Stripe to be charged the new amounts on future invoices.

  • Adam Czajczyk
    • Support Gorilla

    Hello David @ BBS

    I hope you’re well today!

    The payment subscription is always created on Stripe’s end, not the plugin end. That means that recurring payments are always initiated by the Stripe on a set basis.

    Changing any payment settings for a membership that already has paying members will affect only the new invoices and not existing ones so if there were already invoices created for some members for future payments (waiting to be paid) they won’t be updated.

    However, if it comes to future invoices – so invoices created in Stripe/by Stripe in future, after those already created are past due – we’ve setup some tests to double-check it. I realize it’s a pressing issue but we need to test what’s happening and why. Due to the Stripe subscription invoicing process the shortest testing time we were able to set means we need to wait for results until the day after tomorrow and we’ll know more about the issue. I’m sorry it will take time and I’d appreciate some patience.

    We’ll update you here as soon as we get to know more about the case.

    Kind regards,

    Adam

  • David @ BBS
    • Site Builder, Child of Zeus

    Hi Adam,

    Since the description on this ticket create by your colleague is very vague and incorrectly describes the issue I will re-write it here:

    Changing the price of a membership plan updates nicely in the Membership 2 plugin itself. And as you described, all future invoices are generated with the updated price. Running the “&fixsub=1” workaround re-synchronizes the plans with Stripe, which effectively updates the plan’s price on Stripe, however, all existing customers continue to be charged the old price, Stripe doesn’t update their price.

    I have since reached out to Stripe to assist with this and got clarification as to what the exact problem is. It looks like the plugin deletes the old plan and created a new one with the same ID but a new price. Unfortunately, this causes a bunch of issues, including different users at different price points. The reason being, that existing users have to be updated to the new plan, but never are. Ultimately it can be done manually, by going to each users’ subscription page and hitting the “update subscription” button and saving without generating a new invoice, or (in the case of Membership 2) should be done via the API, where all users on the old plan are updated to the new plan (with the new price). See here for details on getting that setup via the API:

    https://stripe.com/docs/billing/subscriptions/products-and-plans#changing-and-deleting-plans

    Currently, after the price change, we’ve got close to a hundred users that need to be updated to use the new pricing on Stripe. So a fast fix on this would be highly appreciated.

  • David @ BBS
    • Site Builder, Child of Zeus

    And to clarify further, the one thing they’ve pointed out (which the doc above also specifies) is that while updating pricing, the plugin should create a new plan with a new ID on Stripe rather than reusing the old one ID. That’s what caused these phantom user subscriptions.

  • Panos
    • SLS

    Hi David @ BBS ,

    Thanks for all the feedback :slight_smile:

    Of what it seems it wasn’t initially been designed to update the prices for existing Members. For those the Subscription needs to be cancelled and member needs to re-subscribe on that Membership with the new price. To me it makes sense as members have agreed to subscribe to the initial price.

    I’m considered about what you mentioned about phantom user subscriptions. Could you provide more info what you mean about those? They are the Customers with the old Plan and not being charged at all? Could you share some info about what you have noticed about them?

    In case you are still interested, I prepared a mu-plugin that should override default method and would create a new Pricing Plan after updating the price in a Membership. Then it updates all Subscriptions of that Membership to the new Plan, so all Customers are charged the new amount. You can download that mu-plugin from here :

    https://gist.github.com/wpmudev-sls/8ee83a8917d9369ddf142e0d26c68dc4

    unzip it and upload wpmudev-ms-update-stripe-plans.php to your wp-content/mu-plugins folder. You can create that folder if it doesn’t exist.

    This should be first tested on a testing/staging site with dummy members. I have been testing this extensively, but this is an unofficial fix and for your security you need to first test this out before applying on your live site, as this is about payment details.

    Kind regards!

  • Panos
    • SLS

    I also forgot to mention that this requires one more change to do. Open file:

    wp-content/plugins/membership-pro/premium/gateway/stripeplan/class-ms-gateway-stripeplan.php

    and find line :

    static public function get_the_id( $id, $type = 'item' ) {

    Right after that line add:

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

    If this is out of your comfort zone we could do those changes for you, but we need admin and ftp access to a staging site, and we can later on move it to your live site. You can send in the stagin’s site credentials privately through our contact form: https://premium.wpmudev.org/contact/#i-have-a-different-question

    Send in:Subject: “Attn: Panos Lyrakis”

    – Admin login:

    Admin username

    Admin password

    Login url

    – FTP credentials

    host

    username

    password

    (and port if required)

    – link back to this thread for reference

  • David @ BBS
    • Site Builder, Child of Zeus

    Hi Panos!

    Thanks for chiming in with a possible solution. Looks like the new updated

    Take a look at user 651:

    –I subscribed this test user to a Stripe subscription. Worked well.

    –I changed the price on the plan on our website. The new mu-plugin and code took care of creating a new pricing plan for the same membership. Worked really well.

    –However, the user wasn’t re-assigned to the new pricing plan. :disappointed:

    –I ran the “&fixsub=1” re-sync in hopes that that would move the customer over to the new pricing plan, but it didn’t.

    So now we’re just missing the option to switch the customers over to the new updating pricing plan. How can we achieve that?

    _________

    As for the phantom users, in theory, the moving all users to the new pricing plan should take care of these. But let me explain what the “phantom user” issue anyway: ultimately what it means is that we had users who were subscribed to our “Family” plan. When we switched the pricing on the plan, Membership2 plugin created a new plan on Stripe with the same ID, but never updated the users. As such, when I go to https://dashboard.stripe.com/subscriptions I can see all my active subscriptions. Including over two dozen users that are part of the “Family” plan (remember, previously the plugin created new plans with the same product/id, so it shows users who are subscribed to the new and old “Family” plan alike). However, when I try to filter the list by “Plan” –> “Family” to see which users belong to the plan, I only get users who subscribed to the new one. The old one don’t exist at all. And that plan doesn’t show as an option to filter users by. I reached out to Stripe and they explained:

    The reason it’s not populating is due to the plan being changed/updated. The filter will only return subscriptions created after the change to the plan itself, so that won’t work. What you can do though, is to go through the logs of the plan itself and get the subscription ID’s that way. You can then update those subscriptions.

    Which is one way. Alternately, assuming that Membership2 keeps the Stripe Plan ID metadata saved with the user, we can use that to update all the users who are on the old payment plan subscription ID and move them to the new one. So in theory, if we succeed to move the users over, this should move ALL users to this new plan.

  • Panos
    • SLS

    However, the user wasn't re-assigned to the new pricing plan.

    hmm it should. I just tested that again on my test site and it did update the Customer plan. This is happening in the sync_subscription_to_plan() method of the mu-plugin.

    In order to confirm that, you can open Customer's page in stripe (eg in test mode https://dashboard.stripe.com/test/customers/cus_THEID ) and scroll to the Subscriptions section. There click on the 3 dots button to expand the option and click on the Edit Subscription link:

    As you can see in the screenshot, other than the link to the Subscription, it shows the upcoming invoices where the price is already updated.

    Could you double check there and let us know if indeed the plan has not been updated? Also could you share admin and ftp access for the staging site? I suspect that there might be a big number of subscriptions to update and could be timing out at some point. Will need to have a closer look though to double check.

    Kind regards!

  • David @ BBS
    • Site Builder, Child of Zeus

    Hi Panos! Thanks for your reply. Unfortunately, no success. I changed the pricing again and the pricing plan got updated, but the client didn’t get switched to the new pricing plan.

    There’s only one user on the Orchestra Family plan that we’re testing with right now, so nothing should be timing out. Once we move the scripts to the live site, we’ll be moving around 30 users, so hopefully the scripts should account for being able to process that without an issue?

    I have enabled Support Access for the the staging site. I’m emailing you FTP access now.

    Thanks!

  • David @ BBS
    • Site Builder, Child of Zeus

    Panos, you’re a rockstar! Thank you! Yes, this worked like a charm. New pricing plan got created and the test user got switched over to the new pricing plan. WOOHOO! :wink: Thanks for your hard work on this. Really appreciate it.

    Final question for you. Is there any way to add a popup that would ask whether the pricing change should be prorated or not? Currently looks like the request sends the change with “prorate changes” enabled. Meaning the users get prorated for any unused time. Instead we’d like the change to get applied to their next billing cycle. How can we implement that?

    Thanks!

    David

  • David @ BBS
    • Site Builder, Child of Zeus

    Great news, it seems to be working beautifully @panoskatws! Thanks a ton!!

    Having said that, I’m having a different issue. It seems like I can’t get all the pricing plans to synchronize correctly with Stripe. The “Balcony Family” plan is set at $19.99, and shows correctly everywhere on our site. However Stripe shows the plan at $19.98. If I change it to any other price point, it gets updated accordingly on Stripe, but moving it back to $19.99 resets it back to $19.98 on Stripe. Seems like the plan ID in Membership2 database on our site incorrectly saved the data somehow? Or maybe the pricing plan was incorrectly created on Stipe? Cause I’m also noticing that when switching back to that $19.99 plan (which on Stripe shows as $19.98) the user doesn’t get re-assigned to this plan. I thought that running &fixsub=1 would re-create the pricing plans on Stripe, but it didn’t. How can we re-synchronize all the pricing plans to make sure things are all in sync?

    Thanks!

    D

  • David @ BBS
    • Site Builder, Child of Zeus

    Hi Predrag Dubajic, Thanks for chiming in and confirming this is an existing issue.

    Panos, I know you’re busy saving the world, but just wanted to check in with you on the status of this. We have an upcoming price change that we need to go live with on August 31st, so I want to make sure we have all the bugs ironed out by then. Thanks for your help on this.

  • Predrag Dubajic
    • Support

    Hi David,

    It looks like there’s actually a PHP bug that is causing this issue, our developers had a look at it and provided me with a patch that I tested on my end and correctly priced plan was created now.

    In order to apply the patch download the ZIP file attached below, unzip it and place the PHP file inside the wp-content/plugins/membership-pro/premium/gateway/stripeplan/ folder and overwrite the current file.

    After that, updating your Membership plans should show the correct price in Stripe.

    Best regards,

    Predrag

  • David @ BBS
    • Site Builder, Child of Zeus

    Hi Predrag Dubajic . Funny, now that you mentioned it, I remember reporting this issue 2 years ago on the forums. Glad we got it resolved! The updated code worked like a charm. Initially it didn’t work, but then I realized the updated code had to actually go into the mu-plugin that Panos created above. Once I got it there, the numbers work beautiful! Thank you!

    So the only outstanding urgent issue we have right now is the phantom users. I explained this on in my message on June 8th. In short, we have many phantom users who are not being moved over to the new pricing plans. When updating the price, the old version of the plugin removed the old product (let’s say ms-plan-367-4poFKiVHtDzoE8UjeljXI3), and then recreated a new one under the same ID (ms-plan-367-4poFKiVHtDzoE8UjeljXI3) with a new price. This created a problem, where old users were still assigned to a membership plan that was in fact deleted. They never got re-assigned to the new one. So they are subscribed to a phantom membership. Which means that now, when we create a new pricing plan (ms-plan-367-4poFKiVHtDzoE8UjeljXI3-mspcode1564649784), those users aren’t being moved to the new pricing plan. They are still stuck on the phantom plan (ms-plan-367-4poFKiVHtDzoE8UjeljXI3).

    How can we make sure they all get migrated? And this issue applies to multiple memberships that we updated pricing plans on.

    In my conversation with the Stripe techs, they had mentioned that if we have the Stripe subscription ID’s for each of the users we could loop through them and move them to the new current pricing plans that way. Is that something that Membership 2 has saved in the User Metadata?

    I SO APPRECIATE your help on this.

  • Panos
    • SLS

    Still not exactly sure what those Phantom users are tbh.

    If you mean in Stripe Dashboard, the users that were members of a Membership that was upgrades, prior using the mu-plugin shared above, should still exist usingthe old Pricing Plan of Stripe. Are those what you mean by Phantom?

    If so, are they a lot? If they are a small number you should be able to add a new Pricing plan to each one manually via Stripe Dashboard. That can be done by visiting each Subscription in Stripe Dashboard individually and then click on the Update Subscription. In the page that follows you can add and remove Products for that Subscription.

    If there the number of those Customers is big I have prepared a custom snippet that should do update plans automatically. That can be downloaded from here :

    https://gist.github.com/wpmudev-sls/d806596694b5dd44d829c15e289bbaf3

    In that snippet you will need to replace the Plan ids in the following lines:

    protected $from_plan_id = 'ms-plan-16-6QBMmq75SHCJL3pZY2dXCY-mspcode1563267282';
    protected $to_plan_id = 'ms-plan-755-5hO1S2LY951XC70jCMbnQk';

    The first line is the current Plan id which I want to remove and the second one is the plan id I want to add (I could have used better variable names…:wink:.

    Once upload the php file to your wp-content/mu-plugins dir, there should be a new admin menu : Membership 2 > Update Stripe Product Plans.

    On the page that loads after you click on that menu you should see only a button (apologies for the bad UI). If you click on it it should fetch all Subscription from Stripe with the Plan Id you want to remove and update them adding the new one.

    Please do not use this yet on a live site. I have done several tests with this but I would strongly suggest to first try it out on a Staging site. Specifically:

    1. Create a staging site with Membership 2 plugin

    2. Create a recurring membership and give it a price

    3. Add Stripe (test mode) api keys. Also make sure you have set the Webhook url in Stripe. Btw, you can create several accounts with same email in Stripe. Useful for testing on accounts that are not Active with real transactions.

    4. Subscribe a user and pay with Stripe

    5. Confirm the Plan ID set in Stripe for that Customer’s Subscription

    6. Update the price in Membership

    7. Confirm that new Product has been created in Stripe but Customer still has the old Plan ID

    8. Follow instructions for this mu-plugins and then check if the Customer’s Subscription has the new Plan id

    Kind regards!

  • David @ BBS
    • Site Builder, Child of Zeus

    Panos I think that’ll do the trick! Before running the code, I skimmed through it and found one left-over bug: line 106 needs to be updated from: if ( ! ! $this->update_subscription_plan( $subscription->id, 'ms-plan-755-5hO1S2LY951XC70jCMbnQk' ) ) { to instead read: if ( ! ! $this->update_subscription_plan( $subscription->id, $this->to_plan_id ) ) {

    Other than that, everything else looked good. So I ran a test on our staging site and it worked beautifully! Thank you!

    You’re correct, sadly we have dozens of these users that are subscribed to old “phantom” memberships. So moving them manually through the dashboard is too much of a lengthy process. This will help a lot! Thanks!

    You had mentioned “Please do not use this yet on a live site”. Is there anything else that you’re contemplating that should be tested before we run it on the live site?

  • Panos
    • SLS

    You had mentioned “Please do not use this yet on a live site”.

    What you found is exactly the reason we ask for testing in staging environments before moving snippets into live ones, especially if they are custom and regard payments.

    Thanks for spotting that! Good catch :slight_smile:

    Since you tested and confirm it works fine on your staging you can proceed to your live site.

    Something I forgot to mention is that for each subscription that gets updated, it should print it in a custom log file: wp-content/mu-plugins/ms-stripe-plans-[DATE].log

    so you can keep an eye of which have been updated successfully and which failed.

    Kind regards!

  • David @ BBS
    • Site Builder, Child of Zeus

    Panos Yes, I saw the logs. They worked really nice. So I ran the script on the live site. And… It didn’t work. At all. No errors. No attempts. Nothing. I explored the code more and after some debugging found out why. Your script was specifically looking for any users subscribed to a particular Plan ID. However, the whole problem with the phantom users is that although in the Stripe Dashboard they show as being subscribed to that Plan ID, if you try to search users subscribed to that Plan ID (whether in the Dashboard or via the API) this will give you 0 results. As if their subscriptions never existed. Which is why I call them phantom subscriptions.

    Something in the way the old Membership 2 worked when changing plans, made these subscriptions like ghosts. You could see them in Stripe, but if you search for users subscribed to that Plan ID, Stripe reports no results. And that’s why your script was returning 0 users subscribed to that plan, and thus not running at all.

    So I decided to brainstorm another work-around based on your script. After a few hours spent with the API documentation and testing various API requests and results, I finally managed to find a way of solving the phantom user issue. Here’s how:

    I realized that although searching for all subscriptions of a specific plan was giving 0 results, if you listed out a particular user’s subscription, the plan ID was still correctly listed in the object. So I decided to alter your code in the following way:

    1. First, I send an API request to Stripe asking for all the active subscriptions on Stripe.

    2. I take that list, and for each subscription, I check the subscription plan ID. If it matches what I’m looking for, I add that object to the array of subscriptions that I need to modify.

    3. I then run your script as is, but on the modified list of subscriptions.

    Here’s the altered code:

    I added the following method close to the bottom:

    private function fetch_all_subscriptions() {

    $url = "https://api.stripe.com/v1/subscriptions?limit=100";

    $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 )->data;

    }

    I then replaced your original lines 93 & 94:

    $subscriptions = $this->fetch_subscriptions_by_plan_id( $this->from_plan_id );
    $this->update_subscriptions_plans( $subscriptions );

    with the following code:

    $all_my_stripe_subscriptions = $this->fetch_all_subscriptions();
    foreach ($all_my_stripe_subscriptions as $key => $subscription) {
    if ($subscription->items->data[0]->plan->id == $this->from_plan_id) {
    $list_of_subs_to_modify[] = $subscription;
    }
    }
    $subscriptions = $list_of_subs_to_modify;
    $this->update_subscriptions_plans( $subscriptions );

    I ran the modified script and it worked like a charm! Posting all of this here in case it helps anyone else in the future.

    Panos I want to personally thank you for all your extreme work in getting us up and running again. Your dedication to helping out with these particularly weird bugs has been a tremendous help.

  • Panos
    • SLS

    Didn’t come across the case with having such phantom users in the same way. They would still be listed by fetching them by plan id. But really happy you spotted this and managed to get it all right! Thank you for your kind words! Appreciate it :slight_smile:

Thank NAME, for their help.

Let NAME know exactly why they deserved these points.

Gift a custom amount of points.