Front end cancellation with stripe subscription and Gravity Forms

I love gravity forms. It makes creating forms really easy and does so pretty securely with WordPress. It is also seems lightweight and does not take over my site nor create a bunch of CSS that is hard to override. It seems like it was made by smart people 🙂

What I like most is the flexibility and the hooks I can use to do just about anything while being a front end developer. I recently was making a site where a subscription model was needed. I decided to use gravity forms along with the stripe add-on to accomplish this. This article assumes you know the basics of gravity forms and making forms using the add-ons for user registration and stripe.

The Subscription Model

Now I have seen many different type of subscription models. The worst for me is the one where you sign up with a credit card and find out the only way to cancel is by contacting the site. This makes me feel uncomfortable and means I am at the mercy of the site and hope they will cancel my subscription!

Unfortunately, that is all you can do out of the box with gravity forms, as there is no way to let the user cancel their own subscription. This post will show you how to change that.

For the site I was making, I decided that users can sign up and register for free, no credit card required. Then they can subscribe for premium access. Finally, they can cancel their subscription on their own with another form. No need to contact the site owner! I find this more friendly and  professional. Here is how I did it:

(Some people want to subscribe a user and let them register at the same time. I have added how to do that when necessary.)

Update!

An update has been made to the code due to some issues with the gravity forms stripe add-on. The code below has been changed a little on Dec, 12, 2016.

Create the forms

First make your forms. For me it was three.

  • Register Form
  • Stripe Subscription Form
  • Cancel Stripe Subscription Form

You obviously need the gravity form add-ons which only come with a developers license. Otherwise you are on your own for creating the actual stripe subscription and end points and everything…

Register Form

The register form uses the register add-on to easily make a new user. It’s quite simple. Add the new user feed and have it make a new user. There is nothing to do extra for this step.

Stripe Subscription

This form is my second form and is set to run only for logged in users. Here we will need to make our first hook. Gravity forms uses an entry to store all the values submitted and it also holds the key to being able to cancel the subscription. The goal is that after the form is submitted and the user has successfully subscribed, we need to save the entry for this form with the user. This way when the same user goes to cancel we can get to that entry he made and cancel it.

We will need to tie this entry ID to the users ID. To do this I used update_user_meta(). This way we now have a way back to this entry and we can cancel the stripe payment with it in the next step! The hook below runs after the form has been submitted and the entry has been made. For me it was form number 2, so I wrote ‘gform_after_submission_2’. If the ID of your form is 5, put 5. All this code goes in functions.php, unless you made another php file just for forms like me…

add_action( 'gform_after_submission_2', 'snp_subscription', 10, 2 );
function snp_subscription( $entry, $form ) {
  //save the entry id to the current users meta when they subscribe for premium stuff
  update_user_meta($entry['created_by'], 'snp_entry_id', $entry['id']);

  //upgrade the user. Make sure its not an admin testing things out...
  if ( ! user_can($entry['created_by'], 'manage_options') ) {
    wp_update_user( array( 'ID' => $entry['created_by'], 'role' => 'paid_subscriber' ) );
  }
}

You will also need to upgrade your users role or do whatever you need to, to make this user is capable of accessing premium stuff. For me, I used Justin Tadlocks members plugin and added a new role paid_subscriber. I then set the user to that role which has a custom capability I made up. Using WordPress’s function current_user_can() I can set up conditionals now throughout my custom theme, and if the user does not have the special custom capability he will not be able to access certain sections and pages. You probably could have used the user registration add-on for gravity forms to update the user. But, heres another way! wp_update_user()

This will set the user that entered this form to the new paid subscriber role. We get the user ID from the entry under $entry['created_by']. Again this assumes the user who filled out the subscription form is already a logged in user. Otherwise this will not work.

And I just test to make sure the user is not an admin, because if your testing this yourself while signed in, you downgrade yourself and successfully lock yourself out of your site! 🙂 fun.

If the user is not logged in

Ok. So maybe you want to have a user subscribed and get registered at the same time with one form. This means the user does not have an ID yet and so we cannot tie the entry to his ID. For that you will need a different hook. One that runs after the registration is complete and the user has an ID. Here is what you would need instead of the above code:

add_action( 'gform_user_registered', 'add_custom_user_meta', 10, 4 );
function add_custom_user_meta( $user_id, $feed, $entry, $user_pass ) { 
  update_user_meta( $user_id, 'snp_entry_id', $entry['id']);

  //upgrade the user 
  if ( ! user_can($user_id, 'manage_options') ) { 
    wp_update_user( array( 'ID' => $user_id, 'role' => 'paid_subscriber'   )); 
  }
}

The Cancel Form

The cancel form will have a hook that is going to get the current user’s ID, find the entry we stored above and cancel the subscription. After all, the only one using the cancel form should be the current user logged in who is cancelling! The only other way to cancel is manually on the back end and that ignores this form completely anyway.

Below we get the current user’s id, and get the entry ID from form number 2. Then we cancel that thing! The user has been cancelled! This hook works for both type of models.

//user can cancel subscription
add_action( 'gform_after_submission_3', 'snp_cancel_subscription', 10, 2 );
function snp_cancel_subscription( $entry, $form ) {
 
  //get original current users entry id from form 2. NOT this entry id!
  $entry_id = get_user_meta(get_current_user_id(), 'snp_entry_id', true);
 
  //now cancel that entry's subscription
  $old_entry = GFAPI::get_entry( $entry_id );
  $feed = is_wp_error( $old_entry ) || ! function_exists( 'gf_stripe' ) ? false : gf_stripe()->get_payment_feed( $entry );
 
  if ( is_array( $feed ) && rgar( $feed, 'addon_slug' ) == 'gravityformsstripe' && gf_stripe()->cancel( $entry, $feed ) ) {
    gf_stripe()->cancel_subscription( $old_entry, $feed );
    
   //destroy old entry id so they cant cancel twice... not sure its necessary
   update_user_meta(get_current_user_id(), 'snp_entry_id', '');

   //set meta to unsubscribed till period ends. in limbo state
   update_user_meta (get_current_user_id(), 'subscribed_till_end', 'yes');    
  } 
 
}

But he has not been downgraded…yet.  We still need to do that via code. But before we do that there is an issue. What if the user paid for the whole month? He gets cancelled and downgraded right away? That is not good. It should wait until the time is up and then cancel him. Thats why I didn’t downgrade him in the above code. Instead we first give the user a post meta of ‘subscribed_till_end’, knowing they are still subscribed, but will be terminated soon. This helps if you need to do some conditional code somewhere and allow them to continue accessing the site, while telling them they will expire soon.

On my site I check for this meta and if it equals “yes”, (in my case its actually a checkbox, I use pods to make custom fields), I output that the user is still subscribed but will soon be unsubscribed.

Next we need to make sure that Stripe knows to set the cancellation at the end of the period, not right away. To do that we need to add yet another hook. Just make sure your original Stripe subscription feed name is the same here as it is when you made it on the first form. Mine was called Subscribe. This code will run when the code above runs. Specifically when ‘cancel_subscription’ is run.

//make sure the cancellation waits till end of the users period (monthly)
add_filter( 'gform_stripe_subscription_cancel_at_period_end', 'stripe_subscription_cancel_at_period_end', 10, 3 );
function stripe_subscription_cancel_at_period_end( $at_period_end, $entry, $feed ) {
  $feed_name = rgars( $feed, 'meta/feedName' );
  if ( $feed_name == 'Subscribe' ) {
    
     //Added due to issue. dont let this action run right away.
    remove_action( 'gform_post_payment_callback', 'remove_user_privileges' );
    return true;
  }

  return $at_period_end;
}

NOTE: Unfortunately, even though we told stripe to cancel at the end of the period, our next function will run right away and it will downgrade the user without waiting! To fix this I added new code which removes the action ‘gform_post_payment_callback’. This way they don’t get downgraded right away.

Downgrading the user once it has officially been cancelled

So once the user is cancelled we can downgrade them. Here is the hook to do so:

//once its actually cancelled at the end of the period, downgrade users role
add_action( 'gform_post_payment_callback', 'remove_user_privileges', 10, 3 );
function remove_user_privileges( $entry, $action, $result ) {
 if ( ! $result && rgar( $action, 'type' ) == 'cancel_subscription' && strtolower( $entry['payment_status'] ) == 'cancelled' ) {
 
    //$entry here is the original entry used to subscribe (form 2). not the cancel $entry.
 
    //make sure your not downgrading an admin somehow!
    if ( ! user_can($entry['created_by'], 'manage_options') ) {
      wp_update_user( array( 'ID' => $entry['created_by'], 'role' => 'subscriber' ) );
    }
  }
 
}

This will cancel your users subscription and the best part is the user did it themselves from a form on the front! Success!! Even better it waits till the users term has fully ended. Just awesome! And no bloated membership plugin needed!!

Cancel for users who signed up and subscribed at the same time

Once again if your user signed up and subscribed at the same time with one form, this wont work as it looks for the user id in the entry. So lets make a few changes:

//once its actually cancelled at the end of the period, downgrade users roleadd_action( 'gform_post_payment_callback', 'remove_user_privileges', 10, 3 );
function remove_user_privileges( $entry, $action, $result ) {
 if ( ! $result && rgar( $action, 'type' ) == 'cancel_subscription' && strtolower( $entry['payment_status'] ) == 'cancelled' ) { 
  //$entry here is the original entry used to subscribe (form 2). not the cancel $entry.
 
  //get the user id a different way because he is not in the entry
  $user_id = gf_user_registration()->get_user_by_entry_id( $entry['id'], true );

  //make sure your not downgrading an admin somehow!
  if ( ! user_can($user_id, 'manage_options') ) {
    wp_update_user( array( 'ID' => $user_id, 'role' => 'subscriber' ) );
  }
 }
}

Well there you have it. A working stripe cancellation from the front. Hope this helps someone out there… It sure helped me!

11 thoughts on “Front end cancellation with stripe subscription and Gravity Forms

    1. shamai says:

      Thanks.
      And thanks for being my first commenter on my site!
      🙂
      Update: actually your second apparently. I never noticed the first one…

      1. Bryan says:

        Great article. I need some help implementing this, you for hire sir? In my case, users sign up for subscription 99% of the time. I’m currently using the basic default STRIPE modal, they pay and get email from STRIPE. They are not logged in or registered in any way on the site at this point. Now I have headache updated expired cards, or cancelling. I’d like a simple and clean way to allow them to pay, register, and update card or cancel (without my involvement). Let me know!!!

      2. shamai says:

        Are you using gravity forms? Are you on WordPress?
        Just follow the article 🙂

        If your using the basic stripe, thats a whole different thing…

  1. Kevin says:

    Hi there. Thanks for posting this article. It has been very helpful! I *think* you have a bug in your cancellation code, though. These two lines refer to $entry instead of $old_entry. To get this function to work, I had to change these two instances of the $entry variable. Can you confirm that this is correct?

    $feed = is_wp_error( $old_entry ) || ! function_exists( ‘gf_stripe’ ) ? false : gf_stripe()->get_payment_feed( $entry );

    if ( is_array( $feed ) && rgar( $feed, ‘addon_slug’ ) == ‘gravityformsstripe’ && gf_stripe()->cancel( $entry, $feed ) ) {

    Also, you have a line that says:

    gf_stripe()->cancel_subscription( $old_entry, $feed );

    I can’t find documentation on the cancel_subscription() method although I can find the docs on the cancel() method used on the preceding line. Am I missing something here? What is this line supposed to do?

    Thanks!
    Kevin

  2. Chantelle says:

    Hi
    Just wonder if you could explain what form fields I need?
    I am doing it so only Logged in members can subscribe.

    So My registration form has Payment card details on and thats it?

    My cancellation form should have what on it or Just a submit button?

    Thanks

  3. Adam says:

    This is exactly what I needed, I’ve tried adding your file from git and as the form submits I get a 500 error. ( the form to actually make the payment, not the cancellation )

    I love the fact it will cancel/downgrade the user at the end of the term ( yearly in our case ) rather than instantly.

  4. FirstSima says:

    I have noticed you don’t monetize your site, don’t waste your traffic, you can earn extra
    cash every month because you’ve got high quality content. If you want to know how to make extra money,
    search for: Boorfe’s tips best adsense alternative

Leave a Reply

Your email address will not be published. Required fields are marked *