DEV Community

Cover image for Create A Subscription System with Stripe and Vue js
Denisse Abreu
Denisse Abreu

Posted on • Updated on • Originally published at codingpr.com

Create A Subscription System with Stripe and Vue js

Hello 🖐🏼, in this guide, I will show you how to build a subscription system with Vue.js and Stripe. Our working environment is Vue.js Options API, Vue Router, and Express.js as a backend manager. For the UI/UX, I'm using Vuetify. If you are looking for the Vue 3 Composition API and TypeScript integration, check this tutorial Create a subscription system with Vue 3, Vite, and Stripe. Due to the extensiveness of this tutorial, I'm leaving the link to my website.

Build A Subscription System With Stripe and Vue js | CodingPR

Learn how to build A Subscription System With Stripe and Vue js Options API.

favicon codingpr.com

If you don't have a project, you can copy mine here:
vue-stripe-subscriptions

1. Set Up Stripe.

First, let's set up our environment. Copy in your env file your publishable key from the stripe dashboard; you can find this key in the developer's section of the dashboard. Create two products in the dashboard products section, the Basic plan for five dollars and the Premium plan for ten. Copy the products IDs in the env file.


      VUE_APP_STRIPE_KEY=<YOUR-PUBLISHABLE-KEY>
      VUE_APP_BASIC_PLAN=<YOUR-BASIC-PLAN>
      VUE_APP_PREMIUM_PLAN=<YOUR-PREMIUM-PLAN>

Enter fullscreen mode Exit fullscreen mode
  • Once you have opened your account with Stripe, copy the script tag into the head of index.html.

    <head>
      <script src="https://js.stripe.com/v3/"></script>
    </head>

Enter fullscreen mode Exit fullscreen mode

2. Integration.

Our first move to integrate Stripe into Vue.js is making the on-click event when the customer wants to sign up for your subscription service. We are collecting the email and the customer's full name; in production, you should collect additional information, like the customer's address.


    <v-card-actions>
      <v-btn
        id="stripeBtn"
        class="mb-2"
        block
        :loading="loading"
        @click="Signup"
      >
        Sign Up
      </v-btn>
    </v-card-actions>

Enter fullscreen mode Exit fullscreen mode
  • In this try and catch block, we are sending, to the backend, the customer personal information that we collected from the sign-up form. If we get a response, push the plan view with the customer id as a parameter. Check Vue Router Docs on how to set up passing parameters between views.

    // methods
    import PostService from '../post-service'

    async Signup() {
      const { email, fullname } = this
      try {
        const res = await PostService.createCust(
          email,
          fullname
        )

        if (res.data.customer) {
          this.$router.push({
            name:'Plan',
            params: {
              fullName: fullname,
              customerId: res.data.customer
            },
            props: true
          })
        }

      } catch (error) {
        this.alert1 = true;
        this.alertTxt = 'Error, Try again!'
      }
    }

Enter fullscreen mode Exit fullscreen mode
  • Create a file in the root of the src folder, the job of this file is to send http requests to the backend with Axios.

    import axios from 'axios';

    const url = 'http://localhost:3000/api/posts';

    class PostService {
      // Create customer
      static async createCust(email, fullname) {
        const res = await axios.post(url, {
          email, fullname
        });
        return res;
      }
      // Create subscription
      static async createSubs(customerId, priceId) {
        const res = await axios.post(`${url}/subs`, {
          customerId, priceId
        });
        return res;
      }
      // Delete subscription
      static async delete(subscriptionId) {
        const res = await axios.post(`${url}/delete`, {
          subscriptionId,
        });
        return res;
      }
    }
    export default PostService;

Enter fullscreen mode Exit fullscreen mode
  • After receiving the response from the server with the customer id, Vue Router will push the second step; your customer needs to choose a plan. Make two buttons with two different on-click events. One is to subscribe to the five-dollar plan and the other to the ten-dollar plan.

    <v-card-actions>
      <v-btn
        id="btnColor"
        :disabled="disabled"
        class="mx-auto mb-2"
        @click="subsPlan1"
      >
        Select
      </v-btn>
    </v-card-actions>
    <v-card-actions>
      <v-btn
        id="btnColor"
        :disabled="disabled2"
        class="mx-auto mb-2"
        @click="subsPlan2"
      >
        Seclect
      </v-btn>
    </v-card-actions>

Enter fullscreen mode Exit fullscreen mode
  • The create subscription function will receive the parameters of the plan that the customer chooses plus the props from step one. This function will send, to the backend, the customer id and the price id and create the subscription; If the response data is good, the checkout view will be pushed with parameters.

    props: {
      fullName: String,
      customerId: String
    },

    data: () => ({
      disabled: false,
      disabled2: false,
      alert2: false,
      alertTxt: '',
    }),

    methods: {
      async createSubscription(priceId, price, plan) {
        const {fullName, customerId } = this
        try {
          const res = await PostService.createSubs(
            customerId,
            priceId,
          )

          if (res.data) {
            const subscriptionId = res.data.subscriptionId
            const clientSecret = res.data.clientSecret
            this.$router.push({
              name: 'Checkout',
              params: {
                fullName,
                price,
                plan,
                clientSecret,
                subscriptionId
              }
            })
          }

        } catch (err) {
          this.alert2 = true
          this.disabled = false
          this.alertTxt = 'An error has occurred. Try again later'
        }
      },

      async subsPlan1() {
        const priceId = process.env.VUE_APP_BASIC_PLAN
        const price = '5.00'
        const plan = 'Basic'
        this.disabled = true
        this.disabled2 = false
        await this.createSubscription(priceId, price, plan)
      },

      async subsPlan2() {
        const priceId = process.env.VUE_APP_PREMIUM_PLAN
        const price = '10.00'
        const plan = 'Premium'
        this.disabled2 = true
        this.disabled = false
        await this.createSubscription(priceId, price, plan)
      }
    }

Enter fullscreen mode Exit fullscreen mode

3. Mounting The Card Element.

This is the last step of the frontend integration, mount the card element and create the submit event.


    <!-- Stripe Element-->
    <div
      ref="card"
      class="inputCard"
    />

    <!-- Error messages in this element -->
    <div
      id="card-errors"
      role="alert"
    />
    <br>
    <v-alert
      v-model="alert"
      color="red"
      dense
      dismissible
      type="error"
    >
      <!-- alertTxt -->
    </v-alert>
    <v-btn
      id="stripeBtn"
      class="my-3"
      block
      :loading="loading"
      @click="Submit"
    >
      Pay with Stripe
    </v-btn>

Enter fullscreen mode Exit fullscreen mode
  • Use the client secret to access the Stripe function "confirm card payment". Inside this function, send the payment method and the customer billing info; Check the list of parameters you can send to stripe . If the subscription is successful, push thank you view with the subscription id as a parameter.

    import PostService from '../post-service'

    const stripe = window.Stripe(process.env.VUE_APP_STRIPE_KEY)

    // Create an instance of Elements.
    const elements = stripe.elements()
    const style = {
      base: {
        color: '#32325d',
        fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
        fontSmoothing: 'antialiased',
        fontSize: '16px',
        '::placeholder': {
          color: '#aab7c4',
        },
      },
      invalid: {
        color: '#fa755a',
        iconColor: '#fa755a',
      },
    };

    const card = elements.create('card', { style: style })

    export default {
      props: {
        fullName: String,
        price: String,
        plan: String,
        clientSecret: String,
        subscriptionId: String
      },

      mounted() {
        card.mount(this.$refs.card)

        card.on('change', (event) => {
          this.displayError(event)
        })
      },

      methods: {
        displayError(event) {
          const displayError = document.getElementById('card-errors')
          if (event.error) {
            displayError.textContent = event.error.message
          } else {
            displayError.textContent = ''
          }
        },

        async Submit() {
          this.loading = true
          const { clientSecret, fullName, alert, alertTxt, loading } = this
          const result = await stripe.confirmCardPayment(clientSecret, {
            payment_method: {
              type: 'card',
              card: card,
              billing_details: {
                name: fullName,
              }
            }
          })

          if (result.error) {
            alert = true
            alertTxt = result.error.message
            loading = false
          } else {
            // Successful subscription payment
            // The subscription automatically becomes active upon payment.
            this.$router.push({
              name: 'ThankYou',
              params: {
                subscriptionId: this.subscriptionId
              }
            })
          }
        }
      }
    }

Enter fullscreen mode Exit fullscreen mode

Check the backend here: Subscription System With Stripe and Express

Top comments (0)