Subscription Payments #3: Update and Cancel plans via a Manage Billing Screen using Stripe

Subscription Payments #3: Update and Cancel plans via a Manage Billing Screen using Stripe

Languages | Tools Used | Time Saved

Node.js, HTML Stripe 3 weeks → 40 mins

Goodies

Live Demo

This is the Part 3 in the series of guides on adding, managing, and keeping track of subscription payments using Stripe and Mongo for your SaaS app.

Subscription Payments #1: Adding Basic and Pro subscription plans using Stripe
Subscription Payments #2: Keeping track of Customer Billing information using Mongo and Stripe Webhooks
Subscription Payments #4: Access premium content based on subscription plan

Every SaaS app needs subscription payments - a recurring payment. Users need to be able to change or cancel their plans, go through a trial phase to see if they even would like to pay for premium features.

In Part 3, we will:

  • Allow customers to switch between Basic and Pro plans
  • Cancel their subscription plan
  • Exit Survey collecting feedback on why customers are churning

Configure Billing Portal Settings

Enable Allow customers to cancel subscriptions and Allow customers to switch to a differnt pricing plan in the Stripe Portal Settings page here.

image

Add our products so customers can see the other available plans when switching.

image

Add a Privacy Policy, Terms of Service, and a Redirect page. Hit Save .

image

Create a Manage Billing button

We can add a button to views/account.ejs that shows only when the customer is on an active plan. This will give the customer the option to update or cancel their plan.

Let's start by adding a button to views/account.ejs

<% } else{ %>
					// ...
              <p>
                Not happy with your current plan? Cancel or Upgrade by clicking the
                button below.
              </p>
              <button id="manage-billing-button" type="submit">
                Manage Billing
              </button>
<% }

Now create the event listener for when the button is clicked in public/js/account.js

// ..

const manageBillingButton = $('#manage-billing-button')

manageBillingButton.click(function () {
    const requestOptions = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
				customer: customer.billingID
      })
    }

    fetch('/billing', requestOptions)
      .then((response) => response.json())
      .then((result) => window.location.replace(result.url))
      .catch((error) => console.log('error', error))
  })

We create a session for Billing on the server-side in app.js

app.post('/billing', async (req, res) => {
  const { customer } = req.body

  const session = await Stripe.createBillingSession(customer)
  console.log('session', session)

  res.json({ url: session.url })
})

We need to edit the src/connect/stripe.js so we can add the createBillingSession method.

const createBillingSession = async (customer) => {
  const session = await Stripe.billingPortal.sessions.create({
    customer,
    return_url: 'https://localhost:4242'
  })
  return session
}

module.exports = {
  // ...
		createBillingSession 
}

Now let's test it out. Login, buy a plan, and then try to change the plan.

The plan will be changed on Stripe but of course we have to update our database as well.

Update Customer data when plan changes

app.post("/webhook", async (req, res) => {
	  // ..
    case "customer.subscription.updated":
      //started trial
      const user = await UserService.getUserByBillingID(data.customer);

      if (data.plan.id == productToPriceMap.BASIC) {
        console.log("You are talking about basic product");
        user.plan = "basic";
      }

      if (data.plan.id == productToPriceMap.PRO) {
        console.log("You are talking about pro product");
        user.plan = "pro";
      }

      const isOnTrial = data.status === "trialing";

      if (isOnTrial) {
        user.hasTrial = true;
        user.endDate = new Date(data.current_period_end * 1000);
      } else if (data.status === "active") {
        user.hasTrial = false;
        user.endDate = new Date(data.current_period_end * 1000);
      }

      if (data.canceled_at) {
        //cancelled
        console.log("You just canceled the subscription" + data.canceled_at);
        user.plan = "none";
        user.hasTrial = false;
        user.endDate = null;
      }
      console.log("actual", user.hasTrial, data.current_period_end, user.plan);

      await user.save();
      console.log("customer changed", JSON.stringify(data));
      break;
    default:
  }
	// ..
});

Test it out

Go through the login flow with your email address, purchase a plan, and try to cancel it.